{"id":22511145,"url":"https://github.com/renderedtext/logman","last_synced_at":"2025-08-10T03:34:44.757Z","repository":{"id":56893074,"uuid":"113656414","full_name":"renderedtext/logman","owner":"renderedtext","description":"Logman — Lightweight abstraction for formatted logging","archived":false,"fork":false,"pushed_at":"2025-02-20T15:29:45.000Z","size":68,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-07-27T22:50:40.026Z","etag":null,"topics":["gem","logging","logman","logs","semaphore-open-source"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/renderedtext.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-09T09:04:11.000Z","updated_at":"2025-02-20T15:29:49.000Z","dependencies_parsed_at":"2022-08-21T01:20:57.382Z","dependency_job_id":null,"html_url":"https://github.com/renderedtext/logman","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/renderedtext/logman","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderedtext%2Flogman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderedtext%2Flogman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderedtext%2Flogman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderedtext%2Flogman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/renderedtext","download_url":"https://codeload.github.com/renderedtext/logman/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/renderedtext%2Flogman/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269672059,"owners_count":24457112,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gem","logging","logman","logs","semaphore-open-source"],"created_at":"2024-12-07T02:09:36.935Z","updated_at":"2025-08-10T03:34:44.731Z","avatar_url":"https://github.com/renderedtext.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Logman\n\n[![Build Status](https://semaphoreci.com/api/v1/renderedtext/logman/branches/master/badge.svg)](https://semaphoreci.com/renderedtext/logman)\n\nLogman introduces a unified logging format in your project. Every log line is an\n*event* that is logged to the STDOUT or to file.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem \"rt-logman\"\n```\n\n## Basic Usage\n\nTo log an informative message to STDOUT, use the following code snippet:\n\n``` ruby\nLogman.info(\"Hello World\")\n\n# Output:\n# {\"level\":\"INFO\",\"time\":\"2017-12-12 09:33:00 +0000\",\"pid\":10950,\"message\":\"Hello World\"}\n```\n\nEvery log event can be extended with metadata — a hash with key value pairs:\n\n``` ruby\nLogman.info(\"Hello World\", :from =\u003e \"renderedtext\", :to =\u003e \"The World\")\n\n# Output:\n# {\"level\":\"INFO\",\"time\":\"2017-12-12 09:33:21 +0000\",\"pid\":10950,\"message\":\"Hello World\",\"from\":\"renderedtext\",\"to\":\"The World\"}\n```\n\nEvery log event has a severity. In the previous examples we have used `info`. To\nlog an `error` use the following snippet:\n\n``` ruby\nLogman.error(\"Team does not exist\", :owner =\u003e \"renderedtext\", :team_name =\u003e \"z-fightes\")\n\n# Output:\n# {\"level\":\"ERROR\",\"time\":\"2017-12-12 09:33:47 +0000\",\"pid\":10950,\"message\":\"Team does not exist\",\"owner\":\"renderedtext\",\"team_name\":\"z-fightes\"}\n```\n\nLogman supports multiple severity levels:\n\n``` ruby\nLogman.fatal(\"Hello\")\nLogman.error(\"Hello\")\nLogman.warn(\"Hello\")\nLogman.info(\"Hello\")\nLogman.debug(\"Hello\")\n```\n\nWhere the following hierarchy stands:\n\n``` txt\nFATAL \u003e ERROR \u003e WARN \u003e INFO \u003e DEBUG\n```\n\n## Instantiated Loggers\n\nLogs in a class or system component usually share common metadata fields. For\nthis purpose, a new instance of Logman can be created and pre-populated with\nmetadata.\n\nIn the following example, we will add logs to a video processor:\n\n``` ruby\nclass VideoProcessor\n\n  def initialize(video)\n    @logger = Logman.new\n\n    # these fields will appear in every log event\n    @logger.add(:component =\u003e \"video_processor\")\n    @logger.add(:id =\u003e @video.id)\n    @logger.add(:title =\u003e \"Keyboard Cat\")\n  end\n\n  def process\n    @logger.info(\"started\")\n\n    content = load_from_disk(@video.location)\n    @logger.info(\"loaded into memory\", :size =\u003e content.length)\n\n    compressed_content = compress(@video)\n    @logger.info(\"compressed\", :size =\u003e compressed_content.length)\n\n    s3_path = upload_to_s3(@video)\n    @logger.info(\"uploaded to S3\", :s3_path =\u003e s3_path)\n\n    @video.update(:s3_path =\u003e s3_path)\n\n    @logger.info(\"finished\")\n  rescue =\u003e exception\n    @logger.error(\"failed\", :exception =\u003e exception.message)\n\n    raise\n  end\n\nend\n```\n\nIn case of a successful processing of a video, we would see the following:\n\n``` txt\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:35:19 +0000\",\"pid\":10950,\"message\":\"started\",\"component\":\"video_processor\",\"id\":9312,\"title\":\"Keyboard Cat\"}\n{\"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}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:35:44 +0000\",\"pid\":10950,\"message\":\"compressed\",\"component\":\"video_processor\",\"id\":9312,\"title\":\"Keyboard Cat\",\"size\":1312312}\n{\"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\"}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:36:27 +0000\",\"pid\":10950,\"message\":\"finished\",\"component\":\"video_processor\",\"id\":9312,\"title\":\"Keyboard Cat\"}\n```\n\nIn case of an error, we would see the following:\n\n``` txt\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:35:19 +0000\",\"pid\":10950,\"message\":\"started\",\"component\":\"video_processor\",\"id\":9312,\"title\":\"Keyboard Cat\"}\n{\"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\"}\n```\n\nLogman can receive a [Ruby Logger](http://ruby-doc.org/stdlib-2.2.0/libdoc/logger/rdoc/Logger.html)\ninstance to handle output. This is useful if you want to log to a file.\n\n``` ruby\n@logger = Logman.new(:logger =\u003e Logger.new(\"/tmp/out.txt\"))\n\n@logger.info(\"Hello World\")\n\n# =\u003e output goes to /tmp/out.txt\n```\n\nYou can also pass an instance of Rails logger:\n\n``` ruby\n@logger = Logman.new(:logger =\u003e Rails.logger)\n\n@logger.info(\"Hello World\")\n```\n\nOr, you can pass an instance of another Logman. This is useful if you want to\ncreate a new Logman instance with the same fields as the previous instance:\n\n``` ruby\n@api_logger = Logman.new\n@api_logger.add(:version =\u003e \"v2\")\n\n@team_api_logger = Logman.new(:logger =\u003e @api_logger)\n# team logger copied the `version` field\n\n@team_api_logger.info(\"Hello\")\n\n# {\"level\":\"INFO\",\"time\":\"2017-12-12 09:39:07 +0000\",\"pid\":10950,\"message\":\"Hello\",\"version\":\"v2\"}\n```\n\nWith Logman, you can instrument data processing with a logger block. For\nexample, if you want to log the steps in a user sign-up progress:\n\n``` ruby\nLogman.process(\"user-registration\", :username =\u003e \"shiroyasha\") do |logger|\n  user = User.create(params)\n  logger.info(\"User Record Created\")\n\n  SignupEmail.send(user)\n  logger.info(\"Sent signup email\")\n\n  team.add(user)\n  logger.info(\"Added user to a team\", :team_id =\u003e team.id)\nend\n```\n\nThe above will log the following information:\n\n``` txt\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:40:39 +0000\",\"pid\":10950,\"message\":\"user-registration-started\",\"username\":\"shiroyasha\"}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:40:39 +0000\",\"pid\":10950,\"message\":\"User Record Created\",\"username\":\"shiroyasha\"}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:40:39 +0000\",\"pid\":10950,\"message\":\"Sent signup email\",\"username\":\"shiroyasha\"}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:40:39 +0000\",\"pid\":10950,\"message\":\"Added user to a team\",\"username\":\"shiroyasha\",\"team_id\":21}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:40:39 +0000\",\"pid\":10950,\"message\":\"user-registration-finished\",\"username\":\"shiroyasha\"}\n```\n\nIn case of an exception, the error will be logged and re-thrown:\n\n``` ruby\nLogman.process(\"user-registration\", :username =\u003e \"shiroyasha\") do |logger|\n  user = User.create(params)\n  logger.info(\"User Record Created\")\n\n  SignupEmail.send(user)\n  logger.info(\"Sent signup email\")\n\n  raise \"Exception\"\n\n  team.add(user)\n  logger.info(\"Added user to a team\", :team_id =\u003e team)\nend\n```\n\n``` ruby\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:41:27 +0000\",\"pid\":10950,\"message\":\"user-registration-started\",\"username\":\"shiroyasha\"}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:41:27 +0000\",\"pid\":10950,\"message\":\"User Record Created\",\"username\":\"shiroyasha\"}\n{\"level\":\"INFO\",\"time\":\"2017-12-12 09:41:27 +0000\",\"pid\":10950,\"message\":\"Sent signup email\",\"username\":\"shiroyasha\"}\n{\"level\":\"ERROR\",\"time\":\"2017-12-12 09:41:27 +0000\",\"pid\":10950,\"message\":\"Exception\",\"username\":\"shiroyasha\",\"type\":\"RuntimeError\"}\n```\n\n## Development\n\nAfter checking out the repo:\n\n- run `bundle install` to install dependencies\n- run `bundle exec rspec` to run unit specs\n- run `bundle exec rubocop` to check code style\n- run `bundle exec reek` to check code smells\n\nTo release a new version:\n\n- bump the version in `lib/logman/version.rb`\n- run `bundle exec rake release` to release a new version on RubyGems\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.com/renderedtext/logman. This project is intended to be a safe,\nwelcoming space for collaboration, and contributors are expected to adhere to\nthe [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThis software is licensed under [the Apache 2.0 license](LICENSE).\n\n## Code of Conduct\n\nEveryone interacting in the Logman project’s codebases, issue trackers, chat\nrooms and mailing lists is expected to follow the\n[code of conduct](https://github.com/renderedtext/logman/blob/master/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenderedtext%2Flogman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frenderedtext%2Flogman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenderedtext%2Flogman/lists"}