{"id":13582483,"url":"https://github.com/sgreben/tj","last_synced_at":"2025-08-22T05:32:11.187Z","repository":{"id":80988983,"uuid":"118508332","full_name":"sgreben/tj","owner":"sgreben","description":"stdin line timestamps. single binary, no dependencies. osx \u0026 linux \u0026 windows. plays well with jq.","archived":false,"fork":false,"pushed_at":"2018-04-28T16:48:12.000Z","size":291,"stargazers_count":230,"open_issues_count":2,"forks_count":2,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-12-10T10:10:10.709Z","etag":null,"topics":["date","go-template","json","regex","shell","stdin","stdout","timestamp"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sgreben.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-22T20:03:44.000Z","updated_at":"2024-10-13T03:02:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"beb35ecd-7647-4ce0-ae58-16d9f896d3fc","html_url":"https://github.com/sgreben/tj","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgreben%2Ftj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgreben%2Ftj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgreben%2Ftj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgreben%2Ftj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sgreben","download_url":"https://codeload.github.com/sgreben/tj/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230561014,"owners_count":18245324,"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":["date","go-template","json","regex","shell","stdin","stdout","timestamp"],"created_at":"2024-08-01T15:02:45.579Z","updated_at":"2024-12-20T09:07:20.129Z","avatar_url":"https://github.com/sgreben.png","language":"Go","readme":"# tj - stdin line timestamps, JSON-friendly\n\n`tj` timestamps lines read from standard input.\n\n- [Get it](#get-it)\n- [Use it](#use-it)\n    - [JSON output](#json-output)\n    - [Time format](#time-format)\n    - [Template output](#template-output)\n    - [Color output](#color-output)\n    - [JSON input](#json-input)\n    - [Stopwatch regex](#stopwatch-regex)\n    - [Stopwatch regex template](#stopwatch-regex-template)\n    - [Stopwatch condition](#stopwatch-condition)\n- [Example](#example)\n- [Comments](https://github.com/sgreben/tj/issues/1)\n\n\n## Get it\n\nUsing go get:\n\n```bash\ngo get -u github.com/sgreben/tj/cmd/tj\n```\n\nOr [download the binary](https://github.com/sgreben/tj/releases/latest) from the releases page:\n\n```bash\n# Linux\ncurl -L https://github.com/sgreben/tj/releases/download/7.0.0/tj_7.0.0_linux_x86_64.tar.gz | tar xz\n\n# OS X\ncurl -L https://github.com/sgreben/tj/releases/download/7.0.0/tj_7.0.0_osx_x86_64.tar.gz | tar xz\n\n# Windows\ncurl -LO https://github.com/sgreben/tj/releases/download/7.0.0/tj_7.0.0_windows_x86_64.zip\nunzip tj_7.0.0_windows_x86_64.zip\n```\n\nAlso available as a [docker image](https://quay.io/repository/sergey_grebenshchikov/tj?tab=tags):\n\n```bash\ndocker pull quay.io/sergey_grebenshchikov/tj\n```\n\n## Use it\n\n`tj` reads from stdin and writes to stdout.\n\n```text\nUsage of tj:\n  -template string\n    \teither a go template (https://golang.org/pkg/text/template) or one of the predefined template names\n  -time-format string\n    \teither a go time format string or one of the predefined format names (https://golang.org/pkg/time/#pkg-constants)\n  -time-zone string\n    \ttime zone to use (or \"Local\") (default \"UTC\")\n  -match-regex string\n    \ta regex pattern. if given, only tokens matching it (re)start the stopwatch\n  -match-template string\n    \tgo template, used to extract text used for -match-regex\n  -match-condition string\n    \tgo template. if given, only tokens that result in 'true' (re)start the stopwatch\n  -match-buffer\n    \tbuffer lines between matches of -match-regex / -match-condition, copy delta values from final line to buffered lines\n  -match string\n    \talias for -match-template\n  -condition string\n    \talias for -match-condition\n  -regex string\n    \talias for -match-regex\n  -read-json\n    \tparse a sequence of JSON objects from stdin\n  -scale string\n    \teither a sequence of hex colors or one of the predefined color scale names (colors go from fast to slow)\n      (default \"BlueToRed\")\n  -scale-fast duration\n    \tthe lower bound for the color scale (default 100ms)\n  -scale-slow duration\n    \tthe upper bound for the color scale (default 2s)\n  -scale-linear\n    \tuse linear scale (default true)\n  -scale-cube\n    \tuse cubic scale\n  -scale-cubert\n    \tuse cubic root scale\n  -scale-sqr\n    \tuse quadratic scale\n  -scale-sqrt\n    \tuse quadratic root scale\n  -version\n    \tprint version and exit\n```\n\n### JSON output\n\nThe default output format is JSON, one object per line:\n\n```bash\n$ (echo Hello; echo World) | tj\n```\n\n```json\n{\"timeSecs\":1517592179,\"timeNanos\":1517592179895262811,\"time\":\"2018-02-02T18:22:59+01:00\",\"deltaSecs\":0.000016485,\"deltaNanos\":16485,\"delta\":\"16.485µs\",\"totalSecs\":0.000016485,\"totalNanos\":16485,\"total\":\"16.485µs\",\"text\":\"Hello\"}\n{\"timeSecs\":1517592179,\"timeNanos\":1517592179895451948,\"time\":\"2018-02-02T18:22:59+01:00\",\"deltaSecs\":0.000189137,\"deltaNanos\":189137,\"delta\":\"189.137µs\",\"totalSecs\":0.000205622,\"totalNanos\":205622,\"total\":\"205.622µs\",\"text\":\"World\"}\n```\n\n### Time format\n\nYou can set the format of the `time` field using the `-time-format` parameter:\n\n```bash\n$ (echo Hello; echo World) | tj -time-format Kitchen\n```\n\n```json\n{\"timeSecs\":1517592194,\"timeNanos\":1517592194875016639,\"time\":\"6:23PM\",\"deltaSecs\":0.000017142,\"deltaNanos\":17142,\"delta\":\"17.142µs\",\"totalSecs\":0.000017142,\"totalNanos\":17142,\"total\":\"17.142µs\",\"text\":\"Hello\"}\n{\"timeSecs\":1517592194,\"timeNanos\":1517592194875197515,\"time\":\"6:23PM\",\"deltaSecs\":0.000180876,\"deltaNanos\":180876,\"delta\":\"180.876µs\",\"totalSecs\":0.000198018,\"totalNanos\":198018,\"total\":\"198.018µs\",\"text\":\"World\"}\n```\n\nThe [constant names from pkg/time](https://golang.org/pkg/time/#pkg-constants) as well as regular go time layouts are valid values for `-time-format`:\n\n| Name       | Format                              |\n|------------|-------------------------------------|\n| ANSIC      | `Mon Jan _2 15:04:05 2006`          |\n| Kitchen    | `3:04PM`                            |\n| ISO8601    | `2006-01-02T15:04:05Z07:00`         |\n| RFC1123    | `Mon, 02 Jan 2006 15:04:05 MST`     |\n| RFC1123Z   | `Mon, 02 Jan 2006 15:04:05 -0700`   |\n| RFC3339    | `2006-01-02T15:04:05Z07:00`         |\n| RFC3339Nano| `2006-01-02T15:04:05.999999999Z07:00`\n| RFC822     | `02 Jan 06 15:04 MST`               |\n| RFC822Z    | `02 Jan 06 15:04 -0700`             |\n| RFC850     | `Monday, 02-Jan-06 15:04:05 MST`    |\n| RubyDate   | `Mon Jan 02 15:04:05 -0700 2006`    |\n| Stamp      | `Jan _2 15:04:05`                   |\n| StampMicro | `Jan _2 15:04:05.000000`            |\n| StampMilli | `Jan _2 15:04:05.000`               |\n| StampNano  | `Jan _2 15:04:05.000000000`         |\n| UnixDate   | `Mon Jan _2 15:04:05 MST 2006`      |\n\n### Template output\n\nYou can also specify an output template using the `-template` parameter and [go template](https://golang.org/pkg/text/template) syntax:\n\n```bash\n$ (echo Hello; echo World) | tj -template '{{ .I }} {{.TimeSecs}} {{.Text}}'\n```\n\n```json\n0 1516649679 Hello\n1 1516649679 World\n```\n\nThe fields available to the template are specified in the [`token` struct](cmd/tj/main.go#L18).\n\nSome templates are pre-defined and can be used via `-template NAME`:\n\n| Name            | Template                                         |\n|-----------------|--------------------------------------------------|\n| Color           | `{{color .}}█{{reset}} {{.Text}}`                |\n| ColorText       | `{{color .}}{{.Text}}{{reset}}`                  |\n| Delta           | `{{.Delta}} {{.Text}}`                           |\n| DeltaColor      | `{{.Delta}} {{color .}}█{{reset}} {{.Text}}`     |\n| DeltaNanos      | `{{.DeltaNanos}} {{.Text}}`                      |\n| Text            | `{{.Text}}`                                      |\n| Time            | `{{.TimeString}} {{.Text}}`                      |\n| TimeColor       | `{{.TimeString}} {{color .}}█{{reset}} {{.Text}}`|\n| TimeDelta       | `{{.TimeString}} +{{.Delta}} {{.Text}}`          |\n| TimeDeltaNanos  | `{{.TimeString}} +{{.DeltaNanos}} {{.Text}}`     |\n\n### Color output\n\nTo help identify durations at a glance, `tj` maps durations to a color scale. The pre-defined templates `Color` and `ColorText` demonstrate this:\n\n```bash\n$ (echo fast; \n   sleep 1; \n   echo slower; \n   sleep 1.5; \n   echo slow; \n   sleep 2; \n   echo slowest) | tj -template Color\n```\n![Color output](docs/images/colors.png)\n\nThe terminal foreground color can be set by using `{{color .}}` in the output template. The default terminal color can be restored using `{{reset}}`.\n\nThe color scale can be set using the parameters `-scale`, `-scale-fast`, and  `-scale-slow`:\n\n- The `-scale` parameter defines the colors used in the scale.  \n- The `-scale-fast` and `-scale-slow` parameters define the boundaries of the scale: durations shorter than the value of `-scale-fast` are mapped to the leftmost color, durations longer than the value of `-scale-slow` are mapped to the rightmost color.\n\nThe scale is linear by default, but can be transformed:\n\n- `-scale-sqr`, `-scale-sqrt` yields a quadratic (root) scale\n- `-scale-cube`, `-scale-cubert` yields a cubic (root) scale\n\nThere are several pre-defined color scales:\n\n| Name                | Scale                  |\n|---------------------|----------------------- |\n| BlackToPurple       | `#000 -\u003e #F700FF`      |\n| BlackToRed          | `#000 -\u003e #F00`         |\n| BlueToRed           | `#00F -\u003e #F00`         |\n| CyanToRed           | `#0FF -\u003e #F00`         |\n| GreenToRed          | `#0F0 -\u003e #F00`         |\n| GreenToGreenToRed   | `#0F0 -\u003e #0F0 -\u003e #F00` |\n| WhiteToPurple       | `#FFF -\u003e #F700FF`      |\n| WhiteToRed          | `#FFF -\u003e #F00`         |\n| WhiteToBlueToRed    | `#FFF -\u003e #00F -\u003e #F00` |\n\nYou can also provide your own color scale using the same syntax as the pre-defined ones.\n\n### JSON input\n\nUsing `-read-json`, you can tell `tj` to parse stdin as a sequence of JSON objects. The parsed object can be referred to via `.Object`, like this:\n\n```bash\n$ echo '{\"hello\": \"World\"}' | tj -read-json -template \"{{.TimeString}} {{.Object.hello}}\"\n```\n\n```\n2018-01-25T21:55:06+01:00 World\n```\n\nThe exact JSON string that was parsed can be recovered using `.Text`:\n\n```bash\n$ echo '{\"hello\"   :    \"World\"} {   }' | tj -read-json -template \"{{.TimeString}} {{.Text}}\"\n```\n\n```\n2018-01-25T21:55:06+01:00 {\"hello\"   :    \"World\"}\n2018-01-25T21:55:06+01:00 {   }\n```\n\n### Stopwatch regex\n\nSometimes you need to measure the duration between certain *tokens* in the input.\n\nTo help with this, `tj` can match each line against a regular expression and only reset the stopwatch (`delta`, `deltaSecs`, `deltaNanos`) when a line matches. The regular expression can be specified via the `-match-regex` (alias `-regex`) parameter.\n\n### Stopwatch regex template\n\nWhen using `-match-regex`, you can also specify a template `-match-template` (alias `-match`) to extract text from the current token. The output of this template is matched against the stopwatch regex. \n\nThis allows you to use only specific fields of JSON objects as stopwatch reset triggers. For example:\n\n```bash\n$ (echo {}; sleep 1; echo {}; sleep 1; echo '{\"reset\": \"yes\"}'; echo {}) | \n    tj -read-json -match .reset -regex yes -template \"{{.I}} {{.DeltaNanos}}\"\n```\n\n```\n0 14374\n1 1005916918\n2 2017292187\n3 79099\n```\n\nThe output of the match template is stored in the field `.MatchText` of the `token` struct:\n\n```bash\n$ echo '{\"message\":\"hello\"}' | tj -read-json -match-template .message -template \"{{.TimeString}} {{.MatchText}}\"\n```\n\n```\n2018-01-25T22:20:59+01:00 hello\n```\n\n### Stopwatch condition\n\nAdditionally to `-match-regex`, you can specify a `-match-condition` go template. If this template produces the literal string `true`, the stopwatch is reset - \"matches\" of the `-match-condition` are treated like matches of the `-match-regex`.\n\n## Example\n\nFinding the slowest step in a `docker build` (using `jq`):\n\n```bash\n$ cat Dockerfile\nFROM alpine\nRUN echo About to be slow...\nRUN sleep 10\nRUN echo Done being slow\n```\n\n```bash\n$ docker build . |\n    tj -regex ^Step |\n    jq -s 'max_by(.deltaNanos) | {step:.start.text, duration:.delta}'\n```\n\n```json\n{\"step\":\"Step 3/4 : RUN sleep 10\",\"duration\":\"10.602026127s\"}\n```\n\nAlternatively, using color output and buffering:\n\n```bash\n$ docker build . |\n    tj -regex ^Step -match-buffer -template Color -scale-cube\n```\n\n![Docker build with color output](docs/images/docker.png)\n\n## Comments\n\nFeel free to [leave a comment](https://github.com/sgreben/tj/issues/1) or create an issue.\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsgreben%2Ftj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsgreben%2Ftj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsgreben%2Ftj/lists"}