Coder Social home page Coder Social logo

airbrake-grails's Introduction

๐Ÿ‘‹ Hi, I'm Eric

tl;dr: I know how to design, prototype, develop, ship, and maintain enterprise-grade web applications that generate revenue.

Experienced and reliable "jack of all trades" with a strong focus on developing enterprise-grade web applications using Ruby on Rails. Founder of CodeFund, the ethical ad platform for software developers. Adept in taking projects from concept to polished enterprise-grade application in a time efficient way. My career is characterized with several high margin exits and high revenue.

๐Ÿ“š Recent bookmarks

airbrake-grails's People

Contributors

donalmurtagh avatar jonspalmer avatar kaufda avatar spoonman avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

airbrake-grails's Issues

Error in docs

Currently it says:

By default, notifications are sent to Airbrake synchronously

It should read:

By default, notifications are sent to Airbrake asynchronously

Exclude exceptions of certain types

What?

Add a configuration param that can be used to skip notice-sending for certain types of exceptions. For example, imagine I don't want to send a notice for all exceptions of type IllegalArgumentException or IllegalStateException

This could be configured using a list of exception class names:

grails.plugins.airbrake.excludes = ['java.lang.IllegalArgumentException', 'java.lang.IllegalStateException']

or alternatively, one or more exception class name patterns:

grails.plugins.airbrake.excludes = ['java.lang.Illegal.*Exception']

I considered a list of exception classes instead of a list of exception class names, but decided against the former approach because it would require the exception to be on the classpath at compile-time.

Why?

In my application I have a global error handler configured in UrlMappings.groovy

"500"(controller: "errors", action: "serverError")

which does the following (among other things)

class ErrorsController {
  def serverError = {
    log.error 'unhandled exception', request.exception
  }
}

However, this causes the exception to be logged twice in Errbit

  1. Once with the exceptions actual type and place of origin
  2. Once with an exception type of GrailsWrappedRuntimeException. The origin of this second occurrence is the log.error statement in the global error handler

So in my case I want to use this config param to exclude these duplicates:

grails.plugins.airbrake.excludes = ['org.codehaus.groovy.grails.web.errors.GrailsWrappedRuntimeException']

I can imagine there might be other cases where this feature might be useful, e.g. a 3rd party library is throwing an exception that you don't care about (and can't change the code to prevent it).

Make plugin more resilient to server unavailability

It's possible that the client cannot contact the server, for example:

  • in the case of a locally running server, the URL, port, etc. may have been incorrectly configured
  • if using the public Airbrake server, it could be unavailable

If the server is unavailable, the plugin will try to send the notice to the server each time an error occurs and if the notices are being sent synchronously this will block the request in which the error occurs.

It seems the following might improve the plugin's tolerance to unavailability of the server:

  • at startup check if the server is contactable and if not, disable notice sending
  • provide a URL that allows notice sending to be enabled/disabled without restarting the application. Obviously this URL would need to be protected so that only admins can access it, but providing this protection would be the responsibility of whatever security mechanism the application is using.

I will submit a pull request if you think this proposal is worthwhile?

Plugin does't send the actual exception class to airbrake

Wonderful plugin. It sends a lot of good information to airbrake, but I am missing the actual exception that was thrown. It looks like the title is the exception message, and the Backtrace is the full stacktrace except for the final exception. Some configuration I am missing?

Incomplete Docs

It would be really helpful if you could document the purpose of each non-obvious property of AirbrakeAppender, e.g.

  • filtered_keys
  • env
  • supplementer
  • secured
  • varFilter
  • etc.

environment

If the appender is configured as per the example in the docs

        def isProd = Environment.current == Environment.PRODUCTION

        def airbrakeAppender = new grails.plugins.airbrake.AirbrakeAppender(
                name: 'airbrake',
                api_key: '19467cfa091f0eb6e51c6350647bf213',
                filtered_keys: ['password'],
                env: (isProd ? 'production' : 'development'))

then when the app is running in the dev environment, the errors are logged in airbrake in the production environment. I tried using the configuration below instead:

        def airbrakeAppender = new grails.plugins.airbrake.AirbrakeAppender(
                name: 'airbrake',
                api_key: '19467cfa091f0eb6e51c6350647bf213',
                filtered_keys: ['password'],
                env: grails.util.Environment.current.name)

and it seems to resolve this problem. Perhaps it would make sense to use the above as the default value for the env property of AirbrakeAppender?

unhandled exception message

The recent contribution that resolved this issue appears to have introduced a regression with the way unhandled exceptions are recorded. Previously, an action such as:

    def throwException() {
        throw new Exception("Power Cut")
    }

would cause an error to be logged in Airbrake with the message

Power Cut

However, the Airbrake message that's now used for an exception such as the above is:

Exception occurred when processing request: [GET] /ttt/test/throwException Power Cut. Stacktrace follows:

My own opinion is that the exception message (alone) is a more readable and appropriate message to use for each Airbrake error, and in the interest of backward compatibility, we should restore the previous behaviour.

script for airbrake deploy notification

Hi,
Is it possible to write a groovy/grails script which notifies the airbrake about a new deployed (GIT)Version? So this script can be executed manually or by an CIServer like Hudson/Jenkins.

THX.

errors not recorded in airbrake

I've tried using v.0.7.2 of this plugin and the head of the git repository, but no matter which version I use, the errors do not appear when I login to the airbrake website.

I've uploaded the sample application I've created to test this plugin, so you can reproduce the problem. If you run this application, then click on the link on the homepage to generate an error, then login to my airbrake account:

https://kaufda.airbrake.io/projects/82838/errors

email: [email protected]
password: playboy,83

you'll see that no errors are recorded

notifications sent synchronously by default

According to the docs

By default, notifications are sent to Airbrake asynchronously using a thread-pool of size 5.

However, my testing indicates that they are sent synchronously by default. You can verify this by creating a test project with a valid API key. Generate and error from this test project with a breakpoint set in the following method of AirbrakeNotifier

void sendNotice(Notice notice) {
    if (configuration.async) {
        configuration.async(notice, grailsApplication)
    } else {
        sendToAirbrake(notice)
    }
}

You'll see that by default the sendToAirbrake branch is executed which sends notices synchronously. The root cause of the problem is in Configuration where async is initialized

    private configureDefaultAsyncClosure(Map options) {
        def async = (options.async != null) ? options.async : true
        if (!(async instanceof Closure)) {

            ////////////////////////////////////////////////////////////////////////////////////////
            /////////////// problem is here async is falsey
            //////////////////////////////////////////////////////////////////////////////////////// 
            if (async == true ) {
                log.info "configureDefaultAsyncClosure create default async handler with threadPool"
                def threadPoolSize = options.asyncThreadPoolSize ?: 5
                threadPool = Executors.newFixedThreadPool(threadPoolSize)
                options.async = { notice, grailsApplication ->
                    log.debug "submitting notice to threadPool"
                    Runnable sendToAirbrake = {
                        grailsApplication.mainContext.airbrakeNotifier.sendToAirbrake(notice)
                    } as Runnable
                    threadPool.submit(sendToAirbrake)
                }
            } else {
                options.async = null
            }
        }
    }

If you add the config param

grails.plugins.airbrake.async = true

Then notifications are sent asynchronously, so maybe the docs just need to be changed to say that the default is synchronous, though IMO asynchronous would be a better default (which implies there's currently a bug).

stacktrace format

By default the backtrace tab in airbrake shows the stacktrace in the following format

AirbrakeTestController.groovy:8:in `throwException'
PageFragmentCachingFilter.java:195:in `doFilter'
AbstractFilter.java:63:in `doFilter'
ThreadPoolExecutor.java:886:in `runTask'
ThreadPoolExecutor.java:908:in `run'
Thread.java:680:in `run'

Is it possible to show the fully-qualified class name instead? If so, IMO, this should be the default.

Docs inconsistent with latest release

The information in the docs is not consistent with v. 0.7.2. The docs propose using the configuration below:

def isProd = Environment.current == Environment.PRODUCTION

log4j = {
  // Example of changing the log pattern for the default console appender:
  appenders {
    def airbrakeAppender = new grails.plugins.airbrake.AirbrakeAppender (
      name: 'airbrake',
      api_key: 'API_KEY',
      filtered_keys: ['password'],
      env: ((Environment.current == Environment.PRODUCTION) ? 'production' : 'development'),
      enabled: true
    )
  }

  root {
    debug 'stdout', 'airbrake' // This can be added to any log level, not only 'debug'
  }
}

But the AirbrakeAppender class does not have an "enabled" property. Also in the config above, the "isProd" variable is never used.

Furthermore, the docs refer to a test controller that is available at:

http://localhost:8080/airbrakeTest/throwException

This is not present in release 0.7.2, but is present in the head of the git repository, so perhaps you need to release this as a new version?

errors not appearing in airbrake

I followed the instructions for integrating v. 0.7.2, but ran into a compilation error because the class MockUserSupplementer could not be found. I eventually discovered that this class is not included in the latest version (0.7.2) of the plugin available in the Grails repo, so I downloaded the .zip from GitHub and used that instead. This resolved the compilation error, but when I run the application and throw an exception, I don't see anything recorded in Airbrake.

My log4j config currently looks like this:

log4j = {
  // Example of changing the log pattern for the default console appender:
  appenders {
    def airbrakeAppender = new grails.plugins.airbrake.AirbrakeAppender (
      name: 'airbrake',
      api_key: '19467cfa091f0eb6e51c6350647bf213',
      filtered_keys: ['password'],
      env: ((Environment.current == Environment.PRODUCTION) ? 'production' : 'development'),    
      enabled: true
    )
    airbrakeAppender.addSupplementer(new MockUserSupplementer())
  }

  root {
    debug 'stdout', 'airbrake' // This can be added to any log level, not only 'debug'
  }
}

The API key above is the key of the single-user test project I've setup. In case you want to login to Airbrake to check why this isn't working, the details are:

email: [email protected]
password: playboy,83

This is just a test account I'm using to evaluate Airbrake, so I don't mind publishing this info in a public forum :)

Should we provide information for nested exceptions?

Currently airbrake plugin provides information only about top-most exception. I think that we can make it better and provide information about all nested exceptions.

For example, consider hierarchy:

MyConnectionException("foo")
    +---IOException("bar")
        +---SocketException("baz")

I think that we can extend plugin so it reports:

  • errorMessage - 3 messages joined with a newline: foo\nbar\nbaz
  • errorClass - top-most exception class: MyConnectionException
  • backtrace - whole backtrace of 3 exceptions from top-most to bottom-lowest

What do you think? I think it's reasonable and I can provide implementation along with tests.

support sending caught exceptions to Airbrake

Currently, the only errors that are sent to Airbrake are unhandled exceptions. However, it would be very useful to also be able to send handled errors.

One possible way to support this would be for all calls to a Logger at the ERROR level (or above) to also send an error, e.g.

try {
  // some code that throws an exception

} catch (ex) {

  // because ex is logged at the error level, the error is sent to Airbrake
  log.error "Something bad happened", ex
}

So the idea is that in the code above ex is treated identically to an uncaught ex. A property could be added to AirbrakeAppender to indicate whether handled exceptions should also be logged, e.g.

    def airbrakeAppender = new grails.plugins.airbrake.AirbrakeAppender (
      name: 'airbrake',
      api_key: 'API_KEY',
      filtered_keys: ['password'],
      notify_handled_exceptions: org.apache.log4j.Level.ERROR
    )

The config above indicates that exceptions logged at the ERROR level and above should be sent to Airbrake. If this parameter is omitted, only uncaught exceptions are sent, to preserve compatability with previous plugin versions.

send errors asynchronously

Sending the errors synchronously really slows down an app (as the docs acknowledge). I'm not sure I completely understand the asynchronous solution proposed in the docs (which involves Quartz), but it seems a lot more complex than necessary.

Why not just add the following to AirbrakeNotifier

Thread.start {
  // code that submits XML notice to Airbrake (over HTTP)
}

If sending each notice in a separate thread is considered too laissez faire, we could use ExecutorService to create a thread-pool instead,

grails 2.2 upgrade and private Map getCgiData(webRequest)

I am getting the following compilation exception after upgrading to grails 2.2

| Error Compilation error: startup failed:
/Users/john/.grails/2.2.0/projects/fotonotes/plugins/airbrake-0.9.1/src/groovy/grails/plugins/airbrake/Notice.groovy: 281: Mixing private and public/protected methods of the same name causes multimethods to be disabled and is forbidden to avoid surprising behaviour. Renaming the private methods will solve the problem.
 @ line 281, column 5.
       private Map getCgiData(webRequest) {
       ^

Truncate messages that would exceed Airbrake's limit

Airbrake has a 64kb limit on the size of notices. Any notice that exceeds this limit is rejected. This typically happens when a exception message is very large. In my opinion, it's more useful to store a truncated error, rather than omit it from Airbrake completely.

I've implemented this locally for the Grails 2.X version of the plugin, by replacing the airbrakeNotifier Spring bean with my own implementation:

@InheritConstructors
class MessageTruncatingAirbrakeNotifier extends AirbrakeNotifier {

    @PackageScope
    static final MAX_MESSAGE_LENGTH = 50000

    @PackageScope
    static final TRUNCATED_MESSAGE_SUFFIX = "...(truncated)"

    MessageTruncatingAirbrakeNotifier(Configuration configuration) {
        super(configuration)
    }

    @Override
    Notice buildNotice(Map options) {

        Notice notice = super.buildNotice(options)

        if (notice.errorMessage.size() > MAX_MESSAGE_LENGTH) {
            notice.errorMessage = notice.errorMessage.take(MAX_MESSAGE_LENGTH) + TRUNCATED_MESSAGE_SUFFIX
        }

        notice
    }
}

It would be useful if this behaviour was added to the plugin, e.g. if a grails.plugins.airbrake.maxErrorMessageChars config param is defined, then any messages that exceed this size are truncated. If this parameter is not defined, there is no truncation.

If you're willing in principle to accept such a contribution and release a new version of the plugin, I'll submit the above as a PR, along with some specs.

Unable to compile v0.9.1

The latest version cannot compile. Here are my results when attempting to compile via the console:

$ grails compile
| Environment set to development....
| Error Exception occurred trigger event [SetClasspath]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Environment set to development.....
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [PluginInstalled]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [PluginInstalled]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [PluginInstalled]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusUpdate]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)
| Error Exception occurred trigger event [StatusError]: No such property: logScriptTiming for class: grails.util.BuildSettings (Use --stacktrace to see the full trace)

It seems as well that the latest commit which fixes the private/public conflict bug has not been pushed to the Grails plugin repo.

I would fix the issue but I am not sure about the logScriptTiming issue. Please help.

Eric

filter stacktraces

Before the stacktrace is converted to XML, remove all the Groovy/Grails noise from it with

Throwable filtered = new DefaultStackTraceFilterer().filter(unfilteredThrowable)

make environment configuration more flexible

I would like to set Notice.env to the hostname (I realise that the host is already available in Notice.hostname, but this field is not displayed on Errbit's error list screen, whereas the environment is).

AFAIK, the only reliable way to get the hostname is from the request object, e.g.

RequestContextHolder.requestAttributes?.currentRequest?.getHeader('X-Forwarded-Host')

However, if I configure

grails.plugins.airbrake.env = RequestContextHolder.requestAttributes?.currentRequest?.getHeader('X-Forwarded-Host')

this won't work, because there is no request object when this configuration is read. Instead, I would like to be able to do this

grails.plugins.airbrake.env = { RequestAttributes webRequest ->
    webRequest?.currentRequest?.getHeader('X-Forwarded-Host')
}

@cavneb if you've no objection to this (and are willing to release a new version that includes this change), could you let me know, and I'll send a pull-request with the relevant code changes?

I'll make this change such that it's backwards compatible, i.e. the environment will be resolved by passing the current request into the closure above only if a closure is assigned to grails.plugins.airbrake.env.

Grails 1.3.X

I would like to use this plugin in a Grails 1.3.7 app. I can't use the current release because AirbrakeGrailsPlugin.groovy declares

def grailsVersion = "2.0.0 > *"

If I were to fork the plugin, and change this to

def grailsVersion = "1.3.7 > *"

are you aware of any reason why the plugin wouldn't work, or to put the question another way, are you actually using any Grails 2.X specific features?

logging error without exception doesn't work with Errbit

I configured

grails.plugins.airbrake.includeEventsWithoutExceptions = true

and tested

log.error 'something went wrong'

in an application that logs to Errbit, rather than Airbrake. Errbit responds with a HTTP 500 to the data that is sent. My suspicion is that Errbit doesn't like an empty backtrace element:

<backtrace/>

but I think if I change it to

<backtrace></backtrace>

it might work. I guess you don't use Errbit, so I can submit a pull request for this if you wish?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.