Logman introduces a unified logging format in your project. Every log line is an event that is logged to the STDOUT or to file.
Add this line to your application's Gemfile:
gem "rt-logman"To log an informative message to STDOUT, use the following code snippet:
Logman.info("Hello World")
# Output:
# {"level":"INFO","time":"2017-12-12 09:33:00 +0000","pid":10950,"message":"Hello World"}Every log event can be extended with metadata — a hash with key value pairs:
Logman.info("Hello World", :from => "renderedtext", :to => "The World")
# Output:
# {"level":"INFO","time":"2017-12-12 09:33:21 +0000","pid":10950,"message":"Hello World","from":"renderedtext","to":"The World"}Every log event has a severity. In the previous examples we have used info. To
log an error use the following snippet:
Logman.error("Team does not exist", :owner => "renderedtext", :team_name => "z-fightes")
# Output:
# {"level":"ERROR","time":"2017-12-12 09:33:47 +0000","pid":10950,"message":"Team does not exist","owner":"renderedtext","team_name":"z-fightes"}Logman supports multiple severity levels:
Logman.fatal("Hello")
Logman.error("Hello")
Logman.warn("Hello")
Logman.info("Hello")
Logman.debug("Hello")Where the following hierarchy stands:
FATAL > ERROR > WARN > INFO > DEBUGLogs in a class or system component usually share common metadata fields. For this purpose, a new instance of Logman can be created and pre-populated with metadata.
In the following example, we will add logs to a video processor:
class VideoProcessor
def initialize(video)
@logger = Logman.new
# these fields will appear in every log event
@logger.add(:component => "video_processor")
@logger.add(:id => @video.id)
@logger.add(:title => "Keyboard Cat")
end
def process
@logger.info("started")
content = load_from_disk(@video.location)
@logger.info("loaded into memory", :size => content.length)
compressed_content = compress(@video)
@logger.info("compressed", :size => compressed_content.length)
s3_path = upload_to_s3(@video)
@logger.info("uploaded to S3", :s3_path => s3_path)
@video.update(:s3_path => s3_path)
@logger.info("finished")
rescue => exception
@logger.error("failed", :exception => exception.message)
raise
end
endIn case of a successful processing of a video, we would see the following:
{"level":"INFO","time":"2017-12-12 09:35:19 +0000","pid":10950,"message":"started","component":"video_processor","id":9312,"title":"Keyboard Cat"}
{"level":"INFO","time":"2017-12-12 09:35:34 +0000","pid":10950,"message":"loaded into memory","component":"video_processor","id":9312,"title":"Keyboard Cat","size":41241241}
{"level":"INFO","time":"2017-12-12 09:35:44 +0000","pid":10950,"message":"compressed","component":"video_processor","id":9312,"title":"Keyboard Cat","size":1312312}
{"level":"INFO","time":"2017-12-12 09:36:08 +0000","pid":10950,"message":"uploaded to S3","component":"video_processor","id":9312,"title":"Keyboard Cat","s3_path":"s3://hehe/a.mpeg"}
{"level":"INFO","time":"2017-12-12 09:36:27 +0000","pid":10950,"message":"finished","component":"video_processor","id":9312,"title":"Keyboard Cat"}In case of an error, we would see the following:
{"level":"INFO","time":"2017-12-12 09:35:19 +0000","pid":10950,"message":"started","component":"video_processor","id":9312,"title":"Keyboard Cat"}
{"level":"ERROR","time":"2017-12-12 09:35:34 +0000","pid":10950,"message":"failed","component":"video_processor","id":9312,"title":"Keyboard Cat","size":41241241,"exception": "Out of memory"}Logman can receive a Ruby Logger instance to handle output. This is useful if you want to log to a file.
@logger = Logman.new(:logger => Logger.new("/tmp/out.txt"))
@logger.info("Hello World")
# => output goes to /tmp/out.txtYou can also pass an instance of Rails logger:
@logger = Logman.new(:logger => Rails.logger)
@logger.info("Hello World")Or, you can pass an instance of another Logman. This is useful if you want to create a new Logman instance with the same fields as the previous instance:
@api_logger = Logman.new
@api_logger.add(:version => "v2")
@team_api_logger = Logman.new(:logger => @api_logger)
# team logger copied the `version` field
@team_api_logger.info("Hello")
# {"level":"INFO","time":"2017-12-12 09:39:07 +0000","pid":10950,"message":"Hello","version":"v2"}With Logman, you can instrument data processing with a logger block. For example, if you want to log the steps in a user sign-up progress:
Logman.process("user-registration", :username => "shiroyasha") do |logger|
user = User.create(params)
logger.info("User Record Created")
SignupEmail.send(user)
logger.info("Sent signup email")
team.add(user)
logger.info("Added user to a team", :team_id => team.id)
endThe above will log the following information:
{"level":"INFO","time":"2017-12-12 09:40:39 +0000","pid":10950,"message":"user-registration-started","username":"shiroyasha"}
{"level":"INFO","time":"2017-12-12 09:40:39 +0000","pid":10950,"message":"User Record Created","username":"shiroyasha"}
{"level":"INFO","time":"2017-12-12 09:40:39 +0000","pid":10950,"message":"Sent signup email","username":"shiroyasha"}
{"level":"INFO","time":"2017-12-12 09:40:39 +0000","pid":10950,"message":"Added user to a team","username":"shiroyasha","team_id":21}
{"level":"INFO","time":"2017-12-12 09:40:39 +0000","pid":10950,"message":"user-registration-finished","username":"shiroyasha"}In case of an exception, the error will be logged and re-thrown:
Logman.process("user-registration", :username => "shiroyasha") do |logger|
user = User.create(params)
logger.info("User Record Created")
SignupEmail.send(user)
logger.info("Sent signup email")
raise "Exception"
team.add(user)
logger.info("Added user to a team", :team_id => team)
end{"level":"INFO","time":"2017-12-12 09:41:27 +0000","pid":10950,"message":"user-registration-started","username":"shiroyasha"}
{"level":"INFO","time":"2017-12-12 09:41:27 +0000","pid":10950,"message":"User Record Created","username":"shiroyasha"}
{"level":"INFO","time":"2017-12-12 09:41:27 +0000","pid":10950,"message":"Sent signup email","username":"shiroyasha"}
{"level":"ERROR","time":"2017-12-12 09:41:27 +0000","pid":10950,"message":"Exception","username":"shiroyasha","type":"RuntimeError"}After checking out the repo:
- run
bundle installto install dependencies - run
bundle exec rspecto run unit specs - run
bundle exec rubocopto check code style - run
bundle exec reekto check code smells
To release a new version:
- bump the version in
lib/logman/version.rb - run
bundle exec rake releaseto release a new version on RubyGems
Bug reports and pull requests are welcome on GitHub at https://github.com/renderedtext/logman. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
This software is licensed under the Apache 2.0 license.
Everyone interacting in the Logman project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.