{"id":23441672,"url":"https://github.com/al-un/ougai-formatters-customizable","last_synced_at":"2025-04-13T10:43:40.723Z","repository":{"id":56887104,"uuid":"156756948","full_name":"Al-un/ougai-formatters-customizable","owner":"Al-un","description":"Adding a customizable formatters for Ougai library","archived":false,"fork":false,"pushed_at":"2020-07-28T04:32:57.000Z","size":145,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-29T16:09:01.113Z","etag":null,"topics":["logging","ruby"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Al-un.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-08T19:19:20.000Z","updated_at":"2021-06-09T12:02:37.000Z","dependencies_parsed_at":"2022-08-21T00:20:47.706Z","dependency_job_id":null,"html_url":"https://github.com/Al-un/ougai-formatters-customizable","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Al-un%2Fougai-formatters-customizable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Al-un%2Fougai-formatters-customizable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Al-un%2Fougai-formatters-customizable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Al-un%2Fougai-formatters-customizable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Al-un","download_url":"https://codeload.github.com/Al-un/ougai-formatters-customizable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248701995,"owners_count":21148111,"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","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":["logging","ruby"],"created_at":"2024-12-23T17:18:04.878Z","updated_at":"2025-04-13T10:43:40.701Z","avatar_url":"https://github.com/Al-un.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ougai-formatters-customizable\n\n[![Gem Version](https://badge.fury.io/rb/ougai-formatters-customizable.svg)](https://badge.fury.io/rb/ougai-formatters-customizable)\n[![Build Status](https://travis-ci.com/Al-un/ougai-formatters-customizable.svg?branch=master)](https://travis-ci.com/Al-un/ougai-formatters-customizable)\n[![Maintainability](https://api.codeclimate.com/v1/badges/eaf20e90252260db1b68/maintainability)](https://codeclimate.com/github/Al-un/ougai-formatters-customizable/maintainability)\n[![Test Coverage](https://api.codeclimate.com/v1/badges/eaf20e90252260db1b68/test_coverage)](https://codeclimate.com/github/Al-un/ougai-formatters-customizable/test_coverage)\n\nA fully customizable formatters for [Ougai](https://github.com/tilfin/ougai)\nlibrary. Customization is about formatting and colorization\n\n**Formatting**\n\nOugai log printing can be split in three components:\n\n 1. Main log message: usually timestamp, log severity and a message\n 2. Data: the structured logging, represented by a Hash\n 3. Errors\n\n**Colorization**\n\nEach part of the main log message can be colored independently. Colorization can\nbe extended to custom formatters as well.\n\n## Usage\n\nIn your Gemfile, add *ougai-formatters-customizable* and its dependencies:\n\n```ruby\ngem 'amazing_print'\ngem 'ougai'\ngem 'ougai-formatters-customizable'\n```\n\nThen initialize a formatter and assign it to your logger:\n\n```ruby\nformatter           = Ougai::Formatters::Customizable.new\n# See Ougai documentation about how to initialize a Ougai logger\nlogger.formatter    = formatter\n```\n\nThe default *Customizable* configuration is exactly identical to a\n*Ougai::Formatters::Readable* as-of Ougai 1.7.0.\n\n#### Datetime format\n\nInherited from Ruby logger formatters, you can assign a datetime format:\n\n```ruby\nformatter.datetime_format = '%H:%M:%S.%L' # print time only such as '15:42:36.246'\n```\n\n#### Message formatter: `format_msg`\n\nMain log message formatter is a `proc` which takes four arguments:\n\n - [String] severity: log severity. Is in capital letters\n - [String] datetime: log timestamp. Is already formatted according to `datetime_format`.\n   Has to be treated like a String\n - [String] progname: optional program name\n - [Hash] data: structured log data. The main message is logged under the `:msg` key.\n\nCustom message formatter can be assigned at initialization via the key `format_msg`:\n\n```ruby\nformatter = Ougai::Formatters::Customizable.new(\n    format_msg: proc do |severity, datetime, _progname, data|\n        msg = data.delete(:msg)\n        format('%s %s: %s', severity, datetime, msg)\n    end\n)\n```\n\n**Notes**\n\n - It is recommended that this proc removes the `:msg` key from `data` to avoid\n   duplicates\n - Although not mandatory, this formatter aims at outputting a single line String\n\n#### Data formatter: `format_data`\n\nData formatter is a `proc` which takes only `data` as argument. Custom data\nformatter can be assigned at initialization via `format_data` key:\n\n```ruby\nformatter = Ougai::Formatters::Customizable.new(\n    format_data: proc do |data|\n        data.ai # Amazing-print printing\n    end\n)\n```\n\n**Notes**\n\n - Data formatter must return `nil` if `data` is empty.\n - Default data formatter takes the `excluded_fields` option into account. You\n   need to add it to your custom formatter if you want to keep it.\n\n#### Error formatter: `format_err`\n\nError formatter is a `proc` with only `data` as argument and can be assigned at\ninitialization via the `format_err` key:\n\n```ruby\nformatter = Ougai::Formatters::Customizable.new(\n    format_err: proc do |data|\n        next nil unless data.key?(:err)\n\n        err = data.delete(:err)\n        \"  #{err[:name]} (#{err[:message]})\"\n    end\n)\n```\n\n**Notes**\n\n - Error formatter must return `nil` if `data` does not contain the `:err` key\n - Error formatter must remove `:err` key\n - Default error formatter takes the `trace_indent` option into account. You need\n   to add it to your custom formatter if you want to keep it\n\n#### Colorization\n\nColorization is handled by an instance of `Ougai::Formatters::Colors::Configuration`\nand is basically a mapping *subject =\u003e value* to define the colors. Default subject\nare:\n\n - `:severity`: log severity coloring\n - `:datetime`: datetime coloring\n - `:msg`: log main message coloring\n\nYou can add your own subject if you need it in your custom formatters.\n\nValues can have three types:\n\n - String: this color is applied to the subject regardless the situation\n - Hash: the color is defined by log severity. Non defined severity colors are\n   fetched from the `default` severity\n - Symbol: the color is copied from the referenced symbol\n\nExample:\n\n```ruby\ncolor_configuration = Ougai::Formatters::Colors::Configuration.new(\n    severity: {\n      trace:    Ougai::Formatters::Colors::WHITE,\n      debug:    Ougai::Formatters::Colors::GREEN,\n      info:     Ougai::Formatters::Colors::CYAN,\n      warn:     Ougai::Formatters::Colors::YELLOW,\n      error:    Ougai::Formatters::Colors::RED,\n      fatal:    Ougai::Formatters::Colors::PURPLE\n    },\n    msg: :severity,\n    datetime: {\n      default:  Ougai::Formatters::Colors::PURPLE,\n      error:    Ougai::Formatters::Colors::RED,\n      fatal:    Ougai::Formatters::Colors::RED\n    },\n    custom:     Ougai::Formatters::Colors::BLUE\n)\n```\n\n - *Severity* has a different color dependending on log severity\n - Main log *message* color is identical to severity color\n - *Datetime* has a red color for *error* and *fatal* logs. Otherwise it is\n   colored in purple.\n - A *custom* subject is always colored in blue regardless log severity\n\n**Notes**\n\n - If `:severity` is not defined, it is loaded from a default configuration\n - If `:severity` is partially defined, missing severities are fetched from\n   default configuration\n - Circular references are not checked and infinite loops can then be triggered.\n\n## Integration\n\n#### Lograge / Lograge-sql\n\nI initially made this gem to couple Ougai with [lograge](https://github.com/roidrage/lograge)/[lograge-sql](https://github.com/iMacTia/lograge-sql). Lograge logs has to be\nformatted in a way so that our custom formatters can catch it:\n\n```ruby\n# config/initializers/lograge.rb\nconfig.lograge.formatter = Class.new do |fmt|\n    def fmt.call(data)\n        { request: data }\n    end\nend\n```\n\nI chose this format because I am also using Loggly and it is pretty convenient\nto filter by `json.request.*` to fetch Lograge logs.\n\nIf using lograge-sql, make sure that Lograge format it as a Hash so that we can\nleverage our main message formatter and data formatter:\n\n```ruby\n# config/initializers/lograge.rb\n  config.lograge_sql.extract_event = proc do |event|\n    {\n      name: event.payload[:name],\n      duration: event.duration.to_f.round(2),\n      sql: event.payload[:sql]\n    }\n  end\n  config.lograge_sql.formatter = proc do |sql_queries|\n    sql_queries\n  end\n```\n\nWrap everything together example:\n\n```ruby\n# Define our colors\ncolor_configuration = Ougai::Formatters::Colors::Configuration.new(\n    severity: {\n      trace:    Ougai::Formatters::Colors::WHITE,\n      debug:    Ougai::Formatters::Colors::GREEN,\n      info:     Ougai::Formatters::Colors::CYAN,\n      warn:     Ougai::Formatters::Colors::YELLOW,\n      error:    Ougai::Formatters::Colors::RED,\n      fatal:    Ougai::Formatters::Colors::PURPLE\n    },\n    msg: :severity,\n    datetime: {\n      default:  Ougai::Formatters::Colors::PURPLE,\n      error:    Ougai::Formatters::Colors::RED,\n      fatal:    Ougai::Formatters::Colors::RED\n    }\n)\n\n# Lograge specific configuration\nEXCLUDED_FIELD = [:credit_card] # example only\nLOGRAGE_REJECT = [:sql_queries, :sql_queries_count]\n\n# Console formatter configuration\nconsole_formatter = Ougai::Formatters::Customizable.new(\n    format_msg: proc do |severity, datetime, _progname, data|\n        # Remove :msg regardless the outcome\n        msg = data.delete(:msg)\n        # Lograge specfic stuff: do not print sql queries in main log message\n        if data.key?(:request)\n            lograge = data[:request].reject { |k, _v| LOGRAGE_REJECT.include?(k) }\n                                    .map { |key, val| \"#{key}: #{val}\" }\n                                    .join(', ')\n            msg = color_config.color(:msg, lograge, severity)\n        # Standard text\n        else\n            msg = color_config.color(:msg, msg, severity)\n        end\n\n        # Standardize output\n        format('%s %s: %s',\n                color_config.color(:severity, severity, severity),\n                color_config.color(:datetime, datetime, severity),\n                msg)\n    end,\n    format_data: proc do |data|\n        # Lograge specfic stuff: main controller output handled by msg formatter\n        if data.key?(:request)\n            lograge_data = data[:request]\n            # concatenate SQL queries\n            if lograge_data.key?(:sql_queries)\n                lograge_data[:sql_queries].map do |sql_query|\n                    format('%\u003cduration\u003e6.2fms %\u003cname\u003e25s %\u003csql\u003es', sql_query)\n                end\n                .join(\"\\n\")\n            # no queries: nothing to print\n            else\n                nil\n            end\n        # Default styling\n        else\n            # report excluded field parameter here: no need to add it to options\n            EXCLUDED_FIELD.each { |field| data.delete(field) }\n            next nil if data.empty?\n\n            # report plain parameter here: no need to add it to options\n            data.ai(plain: false)\n        end\n    end\n)\nconsole_formatter.datetime_format = '%H:%M:%S.%L' # local development: need only time\n\n# Define console logger\nconsole_logger            = Log::Ougai::Logger.new(STDOUT)\nconsole_logger.formatter  = console_formatter\n\n# Not this gem related: define file logger\nfile_logger               = Log::Ougai::Logger.new(Rails.root.join('log/ougai.log'))\nfile_logger.formatter     = Ougai::Formatters::Bunyan.new\n\n# Extend console logger to file logger\nconsole_logger.extend(Ougai::Logger.broadcast(file_logger))\n\n# Assign Ougai logger\nconfig.logger = console_logger\n```\n\nOutput looks like\n![Screenshot](https://raw.githubusercontent.com/Al-un/ougai-formatters-customizable/master/images/screenshot.png)\n\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/Al-un/ougai-formatters-customizable.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fal-un%2Fougai-formatters-customizable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fal-un%2Fougai-formatters-customizable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fal-un%2Fougai-formatters-customizable/lists"}