overleaf/libraries/logger/logging-manager.coffee

129 lines
4.2 KiB
CoffeeScript
Raw Normal View History

2014-02-17 09:54:20 -05:00
bunyan = require('bunyan')
request = require('request')
2014-02-17 09:54:20 -05:00
module.exports = Logger =
initialize: (name) ->
@defaultLevel = process.env['LOG_LEVEL'] or if process.env["NODE_ENV"] == 'production' then "warn" else "debug"
@loggerName = name
2014-02-17 09:54:20 -05:00
@logger = bunyan.createLogger
name: name
serializers: bunyan.stdSerializers
level: @defaultLevel
if process.env["NODE_ENV"] == 'production'
# check for log level override on startup
@.checkLogLevel()
# re-check log level every minute
checkLogLevel = () => @.checkLogLevel()
setInterval(checkLogLevel, 1000 * 60)
2014-02-17 09:54:20 -05:00
return @
checkLogLevel: () ->
options =
headers:
"Metadata-Flavor": "Google"
uri: "http://metadata.google.internal/computeMetadata/v1/project/attributes/#{@loggerName}-setLogLevelEndTime"
request options, (err, response, body) =>
if parseInt(body) > Date.now()
@logger.level('trace')
else
@logger.level(@defaultLevel)
2015-12-08 05:30:18 -05:00
initializeErrorReporting: (sentry_dsn, options) ->
2015-06-16 11:50:18 -04:00
raven = require "raven"
2015-12-08 05:30:18 -05:00
@raven = new raven.Client(sentry_dsn, options)
@lastErrorTimeStamp = 0 # for rate limiting on sentry reporting
2017-03-23 09:49:01 -04:00
@lastErrorCount = 0
captureException: (attributes, message, level) ->
# handle case of logger.error "message"
if typeof attributes is 'string'
attributes = {err: new Error(attributes)}
# extract any error object
error = attributes.err or attributes.error
# avoid reporting errors twice
for key, value of attributes
return if value instanceof Error && value.reportedToSentry
# include our log message in the error report
if not error?
error = {message: message} if typeof message is 'string'
else if message?
attributes.description = message
# report the error
if error?
# capture attributes and use *_id objects as tags
tags = {}
extra = {}
for key, value of attributes
tags[key] = value if key.match(/_id/) and typeof value == 'string'
extra[key] = value
# capture req object if available
req = attributes.req
if req?
extra.req =
method: req.method
url: req.originalUrl
query: req.query
headers: req.headers
ip: req.ip
# recreate error objects that have been converted to a normal object
if !(error instanceof Error) and typeof error is "object"
newError = new Error(error.message)
for own key, value of error
newError[key] = value
error = newError
# filter paths from the message to avoid duplicate errors in sentry
# (e.g. errors from `fs` methods which have a path attribute)
try
error.message = error.message.replace(" '#{error.path}'", '') if error.path
# send the error to sentry
2017-03-03 11:05:56 -05:00
try
@raven.captureException(error, {tags: tags, extra: extra, level: level})
# put a flag on the errors to avoid reporting them multiple times
for key, value of attributes
value.reportedToSentry = true if value instanceof Error
2017-03-03 11:05:56 -05:00
catch
return # ignore any errors
2017-12-07 04:36:12 -05:00
debug : () ->
@logger.debug.apply(@logger, arguments)
2014-02-17 09:54:20 -05:00
info : ()->
@logger.info.apply(@logger, arguments)
log : ()->
@logger.info.apply(@logger, arguments)
error: (attributes, message, args...)->
@logger.error(attributes, message, args...)
2017-02-22 10:53:52 -05:00
if @raven?
2017-03-23 09:49:01 -04:00
MAX_ERRORS = 5 # maximum number of errors in 1 minute
2017-02-22 10:53:52 -05:00
now = new Date()
2017-03-23 09:49:01 -04:00
# have we recently reported an error?
recentSentryReport = (now - @lastErrorTimeStamp) < 60 * 1000
# if so, increment the error count
if recentSentryReport
@lastErrorCount++
else
@lastErrorCount = 0
2017-02-22 10:53:52 -05:00
@lastErrorTimeStamp = now
2017-03-23 09:49:01 -04:00
# only report 5 errors every minute to avoid overload
2017-03-29 08:34:55 -04:00
if @lastErrorCount < MAX_ERRORS
2017-03-23 09:49:01 -04:00
# add a note if the rate limit has been hit
2017-03-29 08:34:55 -04:00
note = if @lastErrorCount+1 is MAX_ERRORS then "(rate limited)" else ""
2017-03-23 09:49:01 -04:00
# report the exception
@captureException(attributes, message, "error#{note}")
err: () ->
@error.apply(this, arguments)
2014-02-17 09:54:20 -05:00
warn: ()->
@logger.warn.apply(@logger, arguments)
fatal: (attributes, message, callback = () ->) ->
@logger.fatal(attributes, message)
if @raven?
cb = (e) -> # call the callback once after 'logged' or 'error' event
callback()
cb = () ->
@captureException(attributes, message, "fatal")
@raven.once 'logged', cb
@raven.once 'error', cb
else
callback()
2014-02-17 09:54:20 -05:00
Logger.initialize("default-sharelatex")