Files
nexus/sreweekly/articles/4/03-structured-logging.html
2026-09-12 17:23:01 +08:00

142 lines
132 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html class="bg-chrome" lang="en-US"><head><meta charset="utf-8"><meta name="theme-color" content="#1d2021"><!-- Set the theme before first paint — the chrome colors depend on it --><script data-astro-rerun>
;(() => {
const stored = localStorage?.getItem('theme') ?? ''
const theme = ['dark', 'light'].includes(stored) ? stored : 'dark'
document.documentElement.setAttribute('data-theme', theme)
window.localStorage.setItem('theme', theme)
if (theme === 'light') {
document
.querySelector('meta[name="theme-color"]')
?.setAttribute('content', '#eceee9')
}
})()
</script><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"><meta name="generator" content="Astro v7.0.6"><meta name="HandheldFriendly" content="True"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no"><link rel="sitemap" href="/sitemap-index.xml"><link rel="manifest" href="/site.webmanifest"><link rel="alternate" type="application/rss+xml" title="Kartar.Net" href="https://kartar.net/rss.xml"><link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="shortcut icon" href="/favicon.ico"><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><meta name="apple-mobile-web-app-title" content="Kartar.Net"><link rel="manifest" href="/site.webmanifest"><meta name="astro-view-transitions-enabled" content="true"><meta name="astro-view-transitions-fallback" content="animate"><script type="module" src="/_astro/ClientRouter.astro_astro_type_script_index_0_lang.6Spq5I16.js"></script><title>Structured Logging | Kartar.Net</title><meta name="title" content="Structured Logging | Kartar.Net"><meta name="description" content="Why string based logs hurt machine consumption, and how to emit typed structured events from a Rails app with Lograge and Logstash-logger"><meta name="author" content="James Turnbull"><link rel="canonical" href="https://kartar.net/2015/12/structured-logging/"><meta property="og:title" content="Structured Logging"><meta property="og:description" content="Why string based logs hurt machine consumption, and how to emit typed structured events from a Rails app with Lograge and Logstash-logger"><meta property="og:image" content="https://kartar.net/static/1200x630.png"><meta property="og:image:alt" content="Structured Logging"><meta property="og:type" content="article"><meta property="og:locale" content="en-US"><meta property="og:site_name" content="Kartar.Net"><meta property="og:url" content="https://kartar.net/2015/12/structured-logging/"><meta property="og:author" content="James Turnbull"><meta property="article:author" content="James Turnbull"><meta property="article:published_time" content="2015-12-13T00:00:00.000Z"><meta name="twitter:title" content="Structured Logging"><meta name="twitter:description" content="Why string based logs hurt machine consumption, and how to emit typed structured events from a Rails app with Lograge and Logstash-logger"><meta property="twitter:image" content="https://kartar.net/static/1200x630.png"><meta name="twitter:image:alt" content="Structured Logging"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:creator" content="James Turnbull"><meta property="article:tag" content="engineering"><meta property="article:tag" content="logging"><meta property="article:tag" content="lograge"><meta property="article:tag" content="logstash"><meta property="article:tag" content="rails"><meta property="article:tag" content="devops"><link rel="stylesheet" href="/_astro/layout.B_6g3Ijy.css"><style>[data-astro-transition-scope="astro-v2o6jcai-1"] { view-transition-name: tmux-status; }@layer astro { ::view-transition-old(tmux-status) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }::view-transition-new(tmux-status) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; }[data-astro-transition=back]::view-transition-old(tmux-status) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }[data-astro-transition=back]::view-transition-new(tmux-status) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; } }[data-astro-transition-fallback="old"] [data-astro-transition-scope="astro-v2o6jcai-1"],
[data-astro-transition-fallback="old"][data-astro-transition-scope="astro-v2o6jcai-1"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }[data-astro-transition-fallback="new"] [data-astro-transition-scope="astro-v2o6jcai-1"],
[data-astro-transition-fallback="new"][data-astro-transition-scope="astro-v2o6jcai-1"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; }[data-astro-transition=back][data-astro-transition-fallback="old"] [data-astro-transition-scope="astro-v2o6jcai-1"],
[data-astro-transition=back][data-astro-transition-fallback="old"][data-astro-transition-scope="astro-v2o6jcai-1"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }[data-astro-transition=back][data-astro-transition-fallback="new"] [data-astro-transition-scope="astro-v2o6jcai-1"],
[data-astro-transition=back][data-astro-transition-fallback="new"][data-astro-transition-scope="astro-v2o6jcai-1"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; }</style><style>[data-astro-transition-scope="astro-doql6odm-2"] { view-transition-name: tmux-identity; }@layer astro { ::view-transition-old(tmux-identity) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }::view-transition-new(tmux-identity) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; }[data-astro-transition=back]::view-transition-old(tmux-identity) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }[data-astro-transition=back]::view-transition-new(tmux-identity) {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; } }[data-astro-transition-fallback="old"] [data-astro-transition-scope="astro-doql6odm-2"],
[data-astro-transition-fallback="old"][data-astro-transition-scope="astro-doql6odm-2"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }[data-astro-transition-fallback="new"] [data-astro-transition-scope="astro-doql6odm-2"],
[data-astro-transition-fallback="new"][data-astro-transition-scope="astro-doql6odm-2"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; }[data-astro-transition=back][data-astro-transition-fallback="old"] [data-astro-transition-scope="astro-doql6odm-2"],
[data-astro-transition=back][data-astro-transition-fallback="old"][data-astro-transition-scope="astro-doql6odm-2"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeOut; }[data-astro-transition=back][data-astro-transition-fallback="new"] [data-astro-transition-scope="astro-doql6odm-2"],
[data-astro-transition=back][data-astro-transition-fallback="new"][data-astro-transition-scope="astro-doql6odm-2"] {
animation-duration: 180ms;
animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1);
animation-fill-mode: both;
animation-name: astroFadeIn; }</style></head><body class="text-chrome-fg bg-chrome m-0 flex min-h-screen flex-col font-sans"><div class="sticky top-0 z-30"><div class="tmux-titlebar border-chrome-border bg-chrome-surface flex items-center gap-3 border-b px-4 py-1.5"><div class="flex shrink-0 gap-1.5"><span class="inline-block size-3 rounded-full bg-[#ff5f57]" aria-hidden="true"></span><span class="inline-block size-3 rounded-full bg-[#febc2e]" aria-hidden="true"></span><span class="inline-block size-3 rounded-full bg-[#28c840]" aria-hidden="true"></span></div><span class="flex flex-1 items-baseline justify-center gap-0 font-mono text-xs"><a href="/" class="text-chrome-fg hover:text-chrome-accent hover:underline" aria-label="Home">james@kartar</a><span class="text-chrome-faint">:</span><a href="/" class="text-chrome-path hover:text-chrome-accent-alt hover:underline" aria-label="Home">~</a><span class="text-chrome-faint">/</span><a href="/2015" class="text-chrome-path hover:text-chrome-accent-alt hover:underline">2015</a><span class="text-chrome-faint">/</span><a href="/2015/12" class="text-chrome-path hover:text-chrome-accent-alt hover:underline">12</a><span class="text-chrome-faint">/</span><span class="text-chrome-fg">structured-logging</span></span><button type="button" class="identity-toggle text-chrome-muted hover:bg-chrome-fg/10 hover:text-chrome-fg inline-flex shrink-0 items-center justify-center rounded-md p-1 transition-colors md:hidden" aria-label="Toggle identity pane" aria-controls="tmux-identity-aside"><svg width="1em" height="1em" class="size-4" data-icon="lucide:menu"><symbol id="ai:lucide:menu" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5h16M4 12h16M4 19h16"/></symbol><use href="#ai:lucide:menu"></use></svg></button><span class="hidden w-12 shrink-0 md:block" aria-hidden="true"></span></div><div data-astro-transition-persist="tmux-status" data-astro-transition-scope="astro-v2o6jcai-1" class="tmux-status border-chrome-border border-b"><div class="bg-chrome text-chrome-muted flex [scrollbar-width:none] items-center gap-x-3 gap-y-1 overflow-x-auto overflow-y-hidden px-3 py-1.5 font-mono text-xs whitespace-nowrap [-ms-overflow-style:none] [&amp;::-webkit-scrollbar]:hidden"><span class="hidden shrink-0 items-center gap-1.5 sm:flex"><span class="bg-chrome-accent text-chrome rounded-sm px-1.5 py-0.5 text-[10px] font-semibold">K</span><span class="text-chrome-faint">kartar</span></span><span class="text-chrome-divider hidden sm:inline" aria-hidden="true">|</span><nav class="flex flex-1 items-center gap-x-3" aria-label="Primary"><a href="/" class="group inline-flex items-baseline gap-1 transition-colors text-chrome-muted hover:text-chrome-fg"><span class="text-chrome-dim group-hover:text-chrome-faint hidden sm:inline">0:</span><span>home</span></a><a href="/blog" class="group inline-flex items-baseline gap-1 transition-colors text-chrome-muted hover:text-chrome-fg"><span class="text-chrome-dim group-hover:text-chrome-faint hidden sm:inline">1:</span><span>blog</span></a><a href="/tags" class="group inline-flex items-baseline gap-1 transition-colors text-chrome-muted hover:text-chrome-fg"><span class="text-chrome-dim group-hover:text-chrome-faint hidden sm:inline">2:</span><span>tags</span></a><a href="/archives" class="group inline-flex items-baseline gap-1 transition-colors text-chrome-muted hover:text-chrome-fg"><span class="text-chrome-dim group-hover:text-chrome-faint hidden sm:inline">3:</span><span>archives</span></a><a href="/about" class="group inline-flex items-baseline gap-1 transition-colors text-chrome-muted hover:text-chrome-fg"><span class="text-chrome-dim group-hover:text-chrome-faint hidden sm:inline">4:</span><span>about</span></a><a href="/search" class="group inline-flex items-baseline gap-1 transition-colors text-chrome-muted hover:text-chrome-fg"><span class="text-chrome-dim group-hover:text-chrome-faint hidden sm:inline">5:</span><span>search</span></a></nav><button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 -my-2 -me-2 size-8" id="theme-toggle" title="Toggle theme"><svg width="1em" height="1em" class="size-4 dark:hidden" data-icon="lucide:sun"><symbol id="ai:lucide:sun" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M4.93 4.93l1.41 1.41m11.32 11.32l1.41 1.41M2 12h2m16 0h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></g></symbol><use href="#ai:lucide:sun"></use></svg><svg width="1em" height="1em" class="absolute hidden size-4 dark:block" data-icon="lucide:moon"><symbol id="ai:lucide:moon" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></symbol><use href="#ai:lucide:moon"></use></svg><span class="sr-only">Toggle theme</span></button><script type="module">function e(e){document.querySelector(`meta[name="theme-color"]`)?.setAttribute(`content`,e===`light`?`#eceee9`:`#1d2021`)}function t(){let t=document.documentElement,n=t.getAttribute(`data-theme`)===`dark`?`light`:`dark`;t.classList.add(`[&_*]:transition-none`),t.setAttribute(`data-theme`,n),window.getComputedStyle(t).getPropertyValue(`opacity`),requestAnimationFrame(()=>{t.classList.remove(`[&_*]:transition-none`)}),localStorage.setItem(`theme`,n),e(n)}function n(){document.getElementById(`theme-toggle`)?.addEventListener(`click`,t)}n(),document.addEventListener(`astro:after-swap`,()=>{let e=localStorage.getItem(`theme`)||`dark`,t=document.documentElement;t.classList.add(`[&_*]:transition-none`),window.getComputedStyle(t).getPropertyValue(`opacity`),t.setAttribute(`data-theme`,e),requestAnimationFrame(()=>{t.classList.remove(`[&_*]:transition-none`)}),n()});</script></div></div></div><div class="tmux-body relative grid flex-1 grid-cols-1 md:grid-cols-[minmax(220px,260px)_1fr] lg:grid-cols-[minmax(240px,300px)_1fr]"><button type="button" class="identity-backdrop fixed inset-0 z-30 hidden bg-black/50 md:!hidden" aria-label="Close identity pane"></button><aside data-astro-transition-persist="tmux-identity" data-astro-transition-scope="astro-doql6odm-2" id="tmux-identity-aside" class="tmux-identity-aside border-chrome-border bg-chrome fixed inset-y-0 left-0 z-40 w-72 max-w-[80vw] -translate-x-full border-r transition-transform duration-200 md:static md:z-auto md:max-w-none md:translate-x-0 [&amp;.is-open]:translate-x-0"><div class="terminal bg-chrome text-chrome-fg selection:bg-chrome-accent/30 selection:text-chrome-fg flex h-full flex-col gap-y-4 px-5 py-4 font-mono text-[0.82rem] leading-relaxed" data-astro-cid-pymivllb><!-- whitespace-pre content below: reflowing it adds visible whitespace --><!-- prettier-ignore --><div data-astro-cid-pymivllb><div class="whitespace-pre" data-astro-cid-pymivllb><a href="/" aria-label="Home" class="group hover:bg-chrome-accent/15 rounded-sm" data-astro-cid-pymivllb><span class="text-chrome-accent group-hover:text-chrome-accent-alt" data-astro-cid-pymivllb>james@kartar</span><span class="text-chrome-faint" data-astro-cid-pymivllb>:</span><span class="text-chrome-path" data-astro-cid-pymivllb>~</span><span class="text-chrome-faint" data-astro-cid-pymivllb>$ </span></a><span class="cmd" data-cmd="finger james" data-astro-cid-pymivllb>finger james</span><span class="cursor cursor-1" aria-hidden="true" data-astro-cid-pymivllb></span></div><div class="response whitespace-pre" data-line="blank-1" data-astro-cid-pymivllb> </div><div class="response whitespace-pre" data-line="login" data-astro-cid-pymivllb><span class="text-chrome-faint" data-astro-cid-pymivllb>Login:</span> james</div><div class="response whitespace-pre" data-line="name" data-astro-cid-pymivllb><span class="text-chrome-faint" data-astro-cid-pymivllb>Name:</span> James Turnbull</div><div class="response whitespace-pre" data-line="shell" data-astro-cid-pymivllb><span class="text-chrome-faint" data-astro-cid-pymivllb>Shell:</span> /bin/zsh</div><div class="response whitespace-pre" data-line="blank-2" data-astro-cid-pymivllb> </div><div class="response whitespace-pre" data-line="plan" data-astro-cid-pymivllb><span class="text-chrome-active" data-astro-cid-pymivllb>Plan:</span></div><div class="response whitespace-pre italic text-chrome-quote" data-line="quote-1" data-astro-cid-pymivllb> &quot;If I had my hand</div><div class="response whitespace-pre italic text-chrome-quote" data-line="quote-2" data-astro-cid-pymivllb> full of truth, I</div><div class="response whitespace-pre italic text-chrome-quote" data-line="quote-3" data-astro-cid-pymivllb> would take good</div><div class="response whitespace-pre italic text-chrome-quote" data-line="quote-4" data-astro-cid-pymivllb> care how I</div><div class="response whitespace-pre italic text-chrome-quote" data-line="quote-5" data-astro-cid-pymivllb> opened it&quot;</div><div class="response whitespace-pre" data-line="blank-3" data-astro-cid-pymivllb> </div><div class="response whitespace-pre" data-line="prompt-2" data-astro-cid-pymivllb><a href="/" aria-label="Home" class="group hover:bg-chrome-accent/15 rounded-sm" data-astro-cid-pymivllb><span class="text-chrome-accent group-hover:text-chrome-accent-alt" data-astro-cid-pymivllb>james@kartar</span><span class="text-chrome-faint" data-astro-cid-pymivllb>:</span><span class="text-chrome-path" data-astro-cid-pymivllb>~</span><span class="text-chrome-faint" data-astro-cid-pymivllb>$ </span></a><span class="cursor cursor-2" aria-hidden="true" data-astro-cid-pymivllb></span></div></div><div class="border-chrome-border mt-auto border-t pt-3" data-astro-cid-pymivllb><div class="text-chrome-faint" data-astro-cid-pymivllb># profile</div><ul class="mt-1 space-y-0.5" data-astro-cid-pymivllb><li data-astro-cid-pymivllb><a href="https://github.com/jamtur01" class="text-chrome-soft hover:text-chrome-accent group inline-flex items-center gap-2" target="_blank" rel="noopener noreferrer" data-astro-cid-pymivllb><svg width="1em" height="1em" class="text-chrome-faint group-hover:text-chrome-accent size-3.5" data-astro-cid-pymivllb="true" data-icon="lucide:github"><symbol id="ai:lucide:github" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5c.08-1.25-.27-2.48-1-3.5c.28-1.15.28-2.35 0-3.5c0 0-1 0-3 1.5c-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5c-.39.49-.68 1.05-.85 1.65S8.93 17.38 9 18v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></g></symbol><use href="#ai:lucide:github"></use></svg><span data-astro-cid-pymivllb>github</span></a></li><li data-astro-cid-pymivllb><a href="https://twitter.com/kartar" class="text-chrome-soft hover:text-chrome-accent group inline-flex items-center gap-2" target="_blank" rel="noopener noreferrer" data-astro-cid-pymivllb><svg width="1em" height="1em" class="text-chrome-faint group-hover:text-chrome-accent size-3.5" data-astro-cid-pymivllb="true" data-icon="lucide:twitter"><symbol id="ai:lucide:twitter" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6c2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4c-.9-4.2 4-6.6 7-3.8c1.1 0 3-1.2 3-1.2"/></symbol><use href="#ai:lucide:twitter"></use></svg><span data-astro-cid-pymivllb>twitter</span></a></li><li data-astro-cid-pymivllb><a href="https://www.linkedin.com/in/turnbulljames/" class="text-chrome-soft hover:text-chrome-accent group inline-flex items-center gap-2" target="_blank" rel="noopener noreferrer" data-astro-cid-pymivllb><svg width="1em" height="1em" class="text-chrome-faint group-hover:text-chrome-accent size-3.5" data-astro-cid-pymivllb="true" data-icon="lucide:linkedin"><symbol id="ai:lucide:linkedin" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2a2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6M2 9h4v12H2z"/><circle cx="4" cy="4" r="2"/></g></symbol><use href="#ai:lucide:linkedin"></use></svg><span data-astro-cid-pymivllb>linkedin</span></a></li><li data-astro-cid-pymivllb><a href="https://hachyderm.io/@kartar" class="text-chrome-soft hover:text-chrome-accent group inline-flex items-center gap-2" target="_blank" rel="noopener noreferrer" data-astro-cid-pymivllb><svg width="1em" height="1em" class="text-chrome-faint group-hover:text-chrome-accent size-3.5" data-astro-cid-pymivllb="true" data-icon="lucide:at-sign"><symbol id="ai:lucide:at-sign" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></g></symbol><use href="#ai:lucide:at-sign"></use></svg><span data-astro-cid-pymivllb>mastodon</span></a></li><li data-astro-cid-pymivllb><a href="https://bsky.app/profile/kartar.net" class="text-chrome-soft hover:text-chrome-accent group inline-flex items-center gap-2" target="_blank" rel="noopener noreferrer" data-astro-cid-pymivllb><svg width="1em" height="1em" class="text-chrome-faint group-hover:text-chrome-accent size-3.5" data-astro-cid-pymivllb="true" data-icon="lucide:cloud"><symbol id="ai:lucide:cloud" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9"/></symbol><use href="#ai:lucide:cloud"></use></svg><span data-astro-cid-pymivllb>bluesky</span></a></li><li data-astro-cid-pymivllb><a href="mailto:james@ltl.so" class="text-chrome-soft hover:text-chrome-accent group inline-flex items-center gap-2" data-astro-cid-pymivllb><svg width="1em" height="1em" class="text-chrome-faint group-hover:text-chrome-accent size-3.5" data-astro-cid-pymivllb="true" data-icon="lucide:mail"><symbol id="ai:lucide:mail" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m22 7l-8.991 5.727a2 2 0 0 1-2.009 0L2 7"/><rect width="20" height="16" x="2" y="4" rx="2"/></g></symbol><use href="#ai:lucide:mail"></use></svg><span data-astro-cid-pymivllb>email</span></a></li><li data-astro-cid-pymivllb><a href="/rss.xml" class="text-chrome-soft hover:text-chrome-accent group inline-flex items-center gap-2" data-astro-cid-pymivllb><svg width="1em" height="1em" class="text-chrome-faint group-hover:text-chrome-accent size-3.5" data-astro-cid-pymivllb="true" data-icon="lucide:rss"><symbol id="ai:lucide:rss" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/></g></symbol><use href="#ai:lucide:rss"></use></svg><span data-astro-cid-pymivllb>rss</span></a></li></ul></div></div><script type="module">var e=e=>new Promise(t=>setTimeout(t,e));async function t(t,n){for(let r=0;r<n.length;r++)t.textContent=n.slice(0,r+1),await e(50)}async function n(n){let r=n.querySelector(`.cmd`),i=Array.from(n.querySelectorAll(`.response`));if(!r)return;if(n.classList.add(`animate`),window.matchMedia(`(prefers-reduced-motion: reduce)`).matches){i.forEach(e=>e.classList.add(`is-visible`)),n.classList.add(`cursor-1-done`,`cursor-2-on`);return}let a=r.dataset.cmd??``;r.textContent=``,await e(300),await t(r,a),await e(220),n.classList.add(`cursor-1-done`);for(let t of i)t.classList.add(`is-visible`),await e(80);n.classList.add(`cursor-2-on`)}function r(){document.querySelectorAll(`.terminal`).forEach(e=>{e.dataset.animated||(e.dataset.animated=`1`,n(e))})}r(),document.addEventListener(`astro:after-swap`,r),document.addEventListener(`astro:page-load`,r);</script></aside><div class="bg-background text-foreground flex min-h-0 flex-col"><div id="mobile-toc-container" class="w-full xl:hidden"><details class="group"><summary class="flex w-full cursor-pointer items-center justify-between"><div class="mx-auto flex w-full max-w-3xl items-center px-4 py-3"><div class="relative mr-2 size-4"><svg class="h-4 w-4" viewBox="0 0 24 24"><circle class="text-primary/20" cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"></circle><circle id="mobile-toc-progress-circle" class="text-primary" cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2" stroke-dasharray="62.83" stroke-dashoffset="62.83" transform="rotate(-90 12 12)"></circle></svg></div><span id="mobile-toc-current-section" class="text-muted-foreground flex-grow truncate text-sm">Overview</span><span class="text-muted-foreground ml-2"><svg width="1em" height="1em" class="h-4 w-4 transition-transform duration-200 group-open:rotate-180" data-icon="lucide:chevron-down"><symbol id="ai:lucide:chevron-down" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m6 9l6 6l6-6"/></symbol><use href="#ai:lucide:chevron-down"></use></svg></span></div></summary><style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event("astro:load"));})();</script><script>(()=>{var g=Object.defineProperty;var w=(c,s,d)=>s in c?g(c,s,{enumerable:!0,configurable:!0,writable:!0,value:d}):c[s]=d;var l=(c,s,d)=>w(c,typeof s!="symbol"?s+"":s,d);var E=new Set(["__proto__","constructor","prototype"]);{let c={0:t=>y(t),1:t=>d(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(d(t)),5:t=>new Set(d(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>Number.POSITIVE_INFINITY*t},s=t=>{let[p,e]=t;return p in c?c[p](e):void 0},d=t=>t.map(s),y=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([p,e])=>[p,s(e)]));class f extends HTMLElement{constructor(){super(...arguments);l(this,"Component");l(this,"hydrator");l(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let n=this.querySelectorAll("astro-slot"),r={},i=this.querySelectorAll("template[data-astro-template]");for(let o of i){let a=o.closest(this.tagName);a!=null&&a.isSameNode(this)&&(r[o.getAttribute("data-astro-template")||"default"]=o.innerHTML,o.remove())}for(let o of n){let a=o.closest(this.tagName);a!=null&&a.isSameNode(this)&&(r[o.getAttribute("name")||"default"]=o.innerHTML)}let u;try{u=this.hasAttribute("props")?y(JSON.parse(this.getAttribute("props"))):{}}catch(o){let a=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(a+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${a}`,this.getAttribute("props"),o),o}let h;await this.hydrator(this)(this.Component,u,r,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});l(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),n.disconnect(),this.childrenConnectedCallback()},n=new MutationObserver(()=>{var r;((r=this.lastChild)==null?void 0:r.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});n.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}getRetryImportUrl(e){let n=new URL(e,document.baseURI),r=`astro-retry=${Date.now()}`,i=n.hash.replace(/^#/,"");return n.hash=i?`${i}&${r}`:r,n.toString()}async importWithRetry(e){try{return await import(e)}catch(n){return await new Promise(r=>setTimeout(r,1e3)),import(this.getRetryImportUrl(e))}}handleHydrationError(e){let n=this.getAttribute("component-url"),r=new CustomEvent("astro:hydration-error",{cancelable:!0,bubbles:!0,composed:!0,detail:{error:e,componentUrl:n}});this.dispatchEvent(r)&&console.error(`[astro-island] Error hydrating ${n}`,e)}async start(){let e=JSON.parse(this.getAttribute("opts")),n=this.getAttribute("client");if(Astro[n]===void 0){window.addEventListener(`astro:${n}`,()=>this.start(),{once:!0});return}try{await Astro[n](async()=>{let r=this.getAttribute("renderer-url");try{let[i,{default:u}]=await Promise.all([this.importWithRetry(this.getAttribute("component-url")),r?this.importWithRetry(r):Promise.resolve({default:()=>()=>{}})]),h=this.getAttribute("component-export")||"default";if(h.includes(".")){this.Component=i;for(let m of h.split(".")){if(E.has(m)||!this.Component||typeof this.Component!="object"&&typeof this.Component!="function"||!Object.hasOwn(this.Component,m))throw new Error(`Invalid component export path: ${h}`);this.Component=this.Component[m]}}else{if(E.has(h))throw new Error(`Invalid component export path: ${h}`);this.Component=i[h]}return this.hydrator=u,this.hydrate}catch(i){return this.handleHydrationError(i),()=>{}}},e,this)}catch(r){this.handleHydrationError(r)}}attributeChangedCallback(){this.hydrate()}}l(f,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",f)}})();</script><astro-island uid="Z18tw3n" prefix="r7" component-url="/_astro/scroll-area.DDwL9Y4X.js" component-export="ScrollArea" renderer-url="/_astro/client.2xUYKAyK.js" props="{&quot;className&quot;:[0,&quot;mx-auto max-w-3xl&quot;],&quot;data-toc-header-scroll&quot;:[0,true]}" ssr client="load" opts="{&quot;name&quot;:&quot;ScrollArea&quot;,&quot;value&quot;:true}" await-children><div dir="ltr" data-slot="scroll-area" class="relative mx-auto max-w-3xl" data-toc-header-scroll="true" style="position:relative;--radix-scroll-area-corner-width:0px;--radix-scroll-area-corner-height:0px"><style>[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}</style><div data-radix-scroll-area-viewport="" data-slot="scroll-area-viewport" class="ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1" style="overflow-x:hidden;overflow-y:hidden"><div style="min-width:100%;display:table"><astro-slot><div class="max-h-[30vh]"><ul class="flex list-none flex-col gap-y-2 px-4 pb-4" id="mobile-table-of-contents"><li class="px-4 text-sm text-foreground/60"><a href="#structured-logging-libraries" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="structured-logging-libraries">Structured logging libraries</a></li><li class="px-4 text-sm ml-4 text-foreground/60"><a href="#java" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="java">Java</a></li><li class="px-4 text-sm ml-4 text-foreground/60"><a href="#go" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="go">Go</a></li><li class="px-4 text-sm ml-4 text-foreground/60"><a href="#clojure" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="clojure">Clojure</a></li><li class="px-4 text-sm ml-4 text-foreground/60"><a href="#ruby--rails" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="ruby--rails">Ruby &amp; Rails</a></li><li class="px-4 text-sm ml-4 text-foreground/60"><a href="#python" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="python">Python</a></li><li class="px-4 text-sm ml-4 text-foreground/60"><a href="#javascript--nodejs" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="javascript--nodejs">Javascript &amp; Node.JS</a></li><li class="px-4 text-sm ml-4 text-foreground/60"><a href="#net" class="mobile-toc-item underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-id="net">.Net</a></li></ul></div></astro-slot></div></div></div><!--astro:end--></astro-island></details></div><script type="module" src="/_astro/toc-header.astro_astro_type_script_index_0_lang.GbCy4XCc.js"></script><main class="mx-auto flex w-full grow flex-col gap-y-6 px-4 py-6"><section class="grid grid-cols-[minmax(0px,1fr)_min(37rem,100%)_minmax(0px,1fr)] gap-y-6"><section class="col-start-2 flex flex-col gap-y-6 text-center"><div class="flex flex-col"><h1 class="mb-2 scroll-mt-31 font-mono text-2xl leading-tight font-semibold tracking-tight sm:text-3xl" id="post-title" data-pagefind-meta="title">Structured Logging</h1><div hidden><span data-pagefind-meta="description">Why string based logs hurt machine consumption, and how to emit typed structured events from a Rails app with Lograge and Logstash-logger</span><span data-pagefind-meta="date">2015-12-13T00:00:00.000Z</span><span data-pagefind-meta="readTime">9 min read</span><span data-pagefind-meta="tags">engineering, logging, lograge, logstash, rails, devops</span></div><div class="text-muted-foreground mb-2 flex flex-wrap items-baseline justify-center gap-x-2 gap-y-1 font-mono text-xs"><time datetime="2015-12-13T00:00:00.000Z" class="tabular-nums">December 13, 2015</time><span class="text-muted-foreground/40" aria-hidden="true">·</span><span class="tabular-nums">9 min read</span></div><div class="flex flex-wrap justify-center gap-x-2 gap-y-1 font-mono text-xs"><a href="/tags/engineering" class="text-primary/85 hover:text-primary hover:underline">#engineering</a><a href="/tags/logging" class="text-primary/85 hover:text-primary hover:underline">#logging</a><a href="/tags/lograge" class="text-primary/85 hover:text-primary hover:underline">#lograge</a><a href="/tags/logstash" class="text-primary/85 hover:text-primary hover:underline">#logstash</a><a href="/tags/rails" class="text-primary/85 hover:text-primary hover:underline">#rails</a><a href="/tags/devops" class="text-primary/85 hover:text-primary hover:underline">#devops</a></div></div><nav class="col-start-2 grid grid-cols-1 gap-x-8 gap-y-4 font-mono text-sm sm:grid-cols-2" aria-label="Post navigation"><a href="/blog/junior-engineers#post-title" class="group text-muted-foreground hover:text-foreground block transition-colors"><div class="text-muted-foreground/70 text-xs sm:text-left"><span class="text-foreground/60">$</span> cd ../prev</div><div class="text-foreground truncate sm:text-left"><span class="text-muted-foreground/70">← </span>So what exactly is a junior software engineer?</div></a><a href="/blog/managing-maintenance-with-riemann#post-title" class="group text-muted-foreground hover:text-foreground block transition-colors"><div class="text-muted-foreground/70 text-xs sm:text-right"><span class="text-foreground/60">$</span> cd ../next</div><div class="text-foreground truncate sm:text-right">Managing maintenance with Riemann<span class="text-muted-foreground/70"> →</span></div></a></nav></section><div id="toc-sidebar-container" class="sticky top-20 col-start-1 row-span-1 mr-8 ml-auto hidden h-[calc(100vh-5rem)] max-w-md xl:block"><astro-island uid="1ah3QY" prefix="r5" component-url="/_astro/scroll-area.DDwL9Y4X.js" component-export="ScrollArea" renderer-url="/_astro/client.2xUYKAyK.js" props="{&quot;className&quot;:[0,&quot;flex max-h-[calc(100vh-8rem)] flex-col overflow-y-auto&quot;],&quot;type&quot;:[0,&quot;hover&quot;],&quot;data-toc-scroll-area&quot;:[0,true]}" ssr client="load" opts="{&quot;name&quot;:&quot;ScrollArea&quot;,&quot;value&quot;:true}" await-children><div dir="ltr" data-slot="scroll-area" class="relative flex max-h-[calc(100vh-8rem)] flex-col overflow-y-auto" data-toc-scroll-area="true" style="position:relative;--radix-scroll-area-corner-width:0px;--radix-scroll-area-corner-height:0px"><style>[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}</style><div data-radix-scroll-area-viewport="" data-slot="scroll-area-viewport" class="ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1" style="overflow-x:hidden;overflow-y:hidden"><div style="min-width:100%;display:table"><astro-slot><div class="flex flex-col gap-2 px-4"><span class="text-lg font-medium">Table of Contents</span><ul class="flex list-none flex-col gap-y-2"><li class="text-sm text-foreground/60"><a href="#structured-logging-libraries" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="structured-logging-libraries">Structured logging libraries</a></li><li class="text-sm ml-4 text-foreground/60"><a href="#java" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="java">Java</a></li><li class="text-sm ml-4 text-foreground/60"><a href="#go" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="go">Go</a></li><li class="text-sm ml-4 text-foreground/60"><a href="#clojure" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="clojure">Clojure</a></li><li class="text-sm ml-4 text-foreground/60"><a href="#ruby--rails" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="ruby--rails">Ruby &amp; Rails</a></li><li class="text-sm ml-4 text-foreground/60"><a href="#python" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="python">Python</a></li><li class="text-sm ml-4 text-foreground/60"><a href="#javascript--nodejs" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="javascript--nodejs">Javascript &amp; Node.JS</a></li><li class="text-sm ml-4 text-foreground/60"><a href="#net" class="marker:text-foreground/30 list-none underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit" data-heading-link="net">.Net</a></li></ul></div></astro-slot></div></div></div><!--astro:end--></astro-island></div><script type="module">var e=new class{links=document.querySelectorAll(`[data-heading-link]`);activeIds=[];headings=[];regions=[];scrollArea=null;tocScrollArea=null;reset(){this.links=document.querySelectorAll(`#toc-sidebar-container [data-heading-link]`),this.activeIds=[],this.headings=[],this.regions=[];let e=document.getElementById(`toc-sidebar-container`);this.scrollArea=e?.querySelector(`[data-radix-scroll-area-viewport]`)||null,this.tocScrollArea=e?.querySelector(`[data-toc-scroll-area]`)||null}},t=class{static build(){if(e.headings=Array.from(document.querySelectorAll(`.prose h2, .prose h3, .prose h4, .prose h5, .prose h6`)),e.headings.length===0){e.regions=[];return}e.regions=e.headings.map((t,n)=>{let r=e.headings[n+1];return{id:t.id,start:t.offsetTop,end:r?r.offsetTop:document.body.scrollHeight}})}static getVisibleIds(){if(e.headings.length===0)return[];let t=window.scrollY+150,n=window.scrollY+window.innerHeight,r=new Set,i=(e,r)=>e>=t&&e<=n||r>=t&&r<=n||e<=t&&r>=n;return e.headings.forEach(e=>{let t=e.offsetTop+e.offsetHeight;i(e.offsetTop,t)&&r.add(e.id)}),e.regions.forEach(e=>{if(e.start<=n&&e.end>=t){let i=document.getElementById(e.id);if(i){let a=i.offsetTop+i.offsetHeight;e.end>a&&(a<n||t<e.end)&&r.add(e.id)}}}),Array.from(r)}},n=class{static update(){if(!e.scrollArea||!e.tocScrollArea)return;let{scrollTop:t,scrollHeight:n,clientHeight:r}=e.scrollArea,i=t<=5,a=t>=n-r-5;e.tocScrollArea.classList.toggle(`mask-t-from-90%`,!i),e.tocScrollArea.classList.toggle(`mask-b-from-90%`,!a)}},r=class{static update(t){e.links.forEach(e=>{e.classList.remove(`text-foreground`)}),t.forEach(e=>{if(e){let t=document.querySelector(`#toc-sidebar-container [data-heading-link="${e}"]`);t&&t.classList.add(`text-foreground`)}}),this.scrollToActive(t)}static scrollToActive(t){if(!e.scrollArea||!t.length)return;let n=document.querySelector(`#toc-sidebar-container [data-heading-link="${t[0]}"]`);if(!n)return;let{top:r,height:i}=e.scrollArea.getBoundingClientRect(),{top:a,height:o}=n.getBoundingClientRect(),s=a-r+e.scrollArea.scrollTop,c=Math.max(0,Math.min(s-(i-o)/2,e.scrollArea.scrollHeight-e.scrollArea.clientHeight));Math.abs(c-e.scrollArea.scrollTop)>5&&(e.scrollArea.scrollTop=c)}},i=class{static handleScroll(){let n=t.getVisibleIds();JSON.stringify(n)!==JSON.stringify(e.activeIds)&&(e.activeIds=n,r.update(e.activeIds))}static handleTOCScroll=()=>n.update();static handleResize(){t.build();let i=t.getVisibleIds();JSON.stringify(i)!==JSON.stringify(e.activeIds)&&(e.activeIds=i,r.update(e.activeIds)),n.update()}static init(){if(e.reset(),t.build(),e.headings.length===0){r.update([]);return}this.handleScroll(),setTimeout(n.update,100);let i={passive:!0};window.addEventListener(`scroll`,this.handleScroll,i),window.addEventListener(`resize`,this.handleResize,i),e.scrollArea?.addEventListener(`scroll`,this.handleTOCScroll,i)}static cleanup(){window.removeEventListener(`scroll`,this.handleScroll),window.removeEventListener(`resize`,this.handleResize),e.scrollArea?.removeEventListener(`scroll`,this.handleTOCScroll),Object.assign(e,{activeIds:[],headings:[],regions:[],scrollArea:null,tocScrollArea:null})}};document.addEventListener(`astro:page-load`,()=>i.init()),document.addEventListener(`astro:after-swap`,()=>{i.cleanup(),i.init()}),document.addEventListener(`astro:before-swap`,()=>i.cleanup());</script><article class="prose col-start-2 max-w-none"><p><em>This is another post triggered by writing <a href="http://artofmonitoring.com" rel="nofollow noreferrer noopener" target="_blank">The Art of Monitoring</a>. You can join the mailing list on that site for further information and updates.</em></p>
<p>Most logging mechanisms emit log entries that contain a string value: the message or description of the error. The classic example of this is Syslog, used by many hosts, services and applications as a default logging format. A typical Syslog message looks like:</p>
<div class="expressive-code"><style>.expressive-code{font-family:var(--ec-uiFontFml);font-size:var(--ec-uiFontSize);font-weight:var(--ec-uiFontWg);line-height:var(--ec-uiLineHt);text-size-adjust:none;-webkit-text-size-adjust:none}.expressive-code *:not(:is(svg, svg *)){all:revert;box-sizing:border-box}.expressive-code pre{display:flex;margin:0;padding:0;border:var(--ec-brdWd) solid var(--ec-brdCol);border-radius:calc(var(--ec-brdRad) + var(--ec-brdWd));background:var(--ec-codeBg)}.expressive-code pre:focus-visible{outline:3px solid var(--ec-focusBrd);outline-offset:-3px}.expressive-code pre > code{all:unset;display:block;flex:1 0 100%;padding:var(--ec-codePadBlk) 0;color:var(--ec-codeFg);font-family:var(--ec-codeFontFml);font-size:var(--ec-codeFontSize);font-weight:var(--ec-codeFontWg);line-height:var(--ec-codeLineHt)}.expressive-code pre{overflow-x:auto}.expressive-code pre.wrap .ec-line .code{white-space:pre-wrap;overflow-wrap:break-word;min-width:min(20ch, var(--ecMaxLine, 20ch))}.expressive-code pre.wrap .ec-line .code span.indent{white-space:pre}.expressive-code pre::-webkit-scrollbar,.expressive-code pre::-webkit-scrollbar-track{background-color:inherit;border-radius:calc(var(--ec-brdRad) + var(--ec-brdWd));border-top-left-radius:0;border-top-right-radius:0}.expressive-code pre::-webkit-scrollbar-thumb{background-color:var(--ec-sbThumbCol);border:4px solid transparent;background-clip:content-box;border-radius:10px}.expressive-code pre::-webkit-scrollbar-thumb:hover{background-color:var(--ec-sbThumbHoverCol)}.expressive-code .ec-line{direction:ltr;unicode-bidi:isolate;display:grid;grid-template-areas:'gutter code';grid-template-columns:auto 1fr;position:relative}.expressive-code .ec-line .gutter{grid-area:gutter;color:var(--ec-gtrFg)}.expressive-code .ec-line .gutter > *{pointer-events:none;user-select:none;-webkit-user-select:none}.expressive-code .ec-line .gutter ~ .code{--ecLineBrdCol:var(--ec-gtrBrdCol)}.expressive-code .ec-line.highlight .gutter{color:var(--ec-gtrHlFg)}.expressive-code .ec-line .code{grid-area:code;position:relative;box-sizing:content-box;padding-inline-start:calc(var(--ecIndent, 0ch) + var(--ec-codePadInl) - var(--ecGtrBrdWd));padding-inline-end:var(--ec-codePadInl);text-indent:calc(var(--ecIndent, 0ch) * -1)}.expressive-code .ec-line .code::before,.expressive-code .ec-line .code::after,.expressive-code .ec-line .code :where(*){text-indent:0}.expressive-code .ec-line .code{--ecGtrBrdWd:var(--ec-gtrBrdWd);border-inline-start:var(--ecGtrBrdWd) solid var(--ecLineBrdCol, transparent)}.expressive-code .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0}.expressive-code .ec-line.mark{--tmLineBgCol:var(--ec-tm-markBg)}.expressive-code .ec-line.mark .code{--ecLineBrdCol:var(--ec-tm-markBrdCol)}.expressive-code .ec-line.ins{--tmLineBgCol:var(--ec-tm-insBg);--tmLabel:var(--ec-tm-insDiffIndContent)}.expressive-code .ec-line.ins .code{--ecLineBrdCol:var(--ec-tm-insBrdCol)}.expressive-code .ec-line.ins .code::before{color:var(--ec-tm-insDiffIndCol)}.expressive-code .ec-line.del{--tmLineBgCol:var(--ec-tm-delBg);--tmLabel:var(--ec-tm-delDiffIndContent)}.expressive-code .ec-line.del .code{--ecLineBrdCol:var(--ec-tm-delBrdCol)}.expressive-code .ec-line.del .code::before{color:var(--ec-tm-delDiffIndCol)}.expressive-code .ec-line.mark,.expressive-code .ec-line.ins,.expressive-code .ec-line.del{background:var(--tmLineBgCol)}.expressive-code .ec-line.mark .code,.expressive-code .ec-line.ins .code,.expressive-code .ec-line.del .code{--ecGtrBrdWd:var(--ec-tm-lineMarkerAccentWd)}.expressive-code .ec-line.mark .code::before,.expressive-code .ec-line.ins .code::before,.expressive-code .ec-line.del .code::before{display:block;position:absolute;left:0;box-sizing:border-box;content:var(--tmLabel, ' ');padding-inline-start:var(--ec-tm-lineDiffIndMargLeft);text-align:center;white-space:pre}.expressive-code .ec-line.mark.tm-label .code::before,.expressive-code .ec-line.ins.tm-label .code::before,.expressive-code .ec-line.del.tm-label .code::before{background:var(--ecLineBrdCol);padding:0 calc(var(--ec-tm-lineMarkerLabelPadInl) + var(--ec-tm-lineMarkerAccentWd)) 0 var(--ec-tm-lineMarkerLabelPadInl);color:var(--ec-tm-lineMarkerLabelCol)}.expressive-code .ec-line mark{--tmInlineBgCol:var(--ec-tm-markBg);--tmInlineBrdCol:var(--ec-tm-markBrdCol)}.expressive-code .ec-line ins{--tmInlineBgCol:var(--ec-tm-insBg);--tmInlineBrdCol:var(--ec-tm-insBrdCol)}.expressive-code .ec-line del{--tmInlineBgCol:var(--ec-tm-delBg);--tmInlineBrdCol:var(--ec-tm-delBrdCol)}.expressive-code .ec-line mark,.expressive-code .ec-line ins,.expressive-code .ec-line del{all:unset;display:inline-block;position:relative;--tmBrdL:var(--ec-tm-inlMarkerBrdWd);--tmBrdR:var(--ec-tm-inlMarkerBrdWd);--tmRadL:var(--ec-tm-inlMarkerBrdRad);--tmRadR:var(--ec-tm-inlMarkerBrdRad);margin-inline:0.025rem;padding-inline:var(--ec-tm-inlMarkerPad);border-radius:var(--tmRadL) var(--tmRadR) var(--tmRadR) var(--tmRadL);background:var(--tmInlineBgCol);background-clip:padding-box}.expressive-code .ec-line mark.open-start,.expressive-code .ec-line ins.open-start,.expressive-code .ec-line del.open-start{margin-inline-start:0;padding-inline-start:0;--tmBrdL:0px;--tmRadL:0}.expressive-code .ec-line mark.open-end,.expressive-code .ec-line ins.open-end,.expressive-code .ec-line del.open-end{margin-inline-end:0;padding-inline-end:0;--tmBrdR:0px;--tmRadR:0}.expressive-code .ec-line mark::before,.expressive-code .ec-line ins::before,.expressive-code .ec-line del::before{content:'';position:absolute;pointer-events:none;display:inline-block;inset:0;border-radius:var(--tmRadL) var(--tmRadR) var(--tmRadR) var(--tmRadL);border:var(--ec-tm-inlMarkerBrdWd) solid var(--tmInlineBrdCol);border-inline-width:var(--tmBrdL) var(--tmBrdR)}.expressive-code .frame{all:unset;position:relative;display:block;--header-border-radius:calc(var(--ec-brdRad) + var(--ec-brdWd));--tab-border-radius:calc(var(--ec-frm-edTabBrdRad) + var(--ec-brdWd));--button-spacing:0.4rem;--code-background:var(--ec-frm-edBg);border-radius:var(--header-border-radius);box-shadow:var(--ec-frm-frameBoxShdCssVal)}.expressive-code .frame .header{display:none;z-index:1;position:relative;border-radius:var(--header-border-radius) var(--header-border-radius) 0 0}.expressive-code .frame.has-title pre,.expressive-code .frame.has-title code,.expressive-code .frame.is-terminal pre,.expressive-code .frame.is-terminal code{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.expressive-code .frame .title:empty:before{content:'\a0'}.expressive-code .frame.has-title:not(.is-terminal){--button-spacing:calc(1.9rem + 2 * (var(--ec-uiPadBlk) + var(--ec-frm-edActTabIndHt)))}.expressive-code .frame.has-title:not(.is-terminal) .title{position:relative;color:var(--ec-frm-edActTabFg);background:var(--ec-frm-edActTabBg);background-clip:padding-box;margin-block-start:var(--ec-frm-edTabsMargBlkStart);padding:calc(var(--ec-uiPadBlk) + var(--ec-frm-edActTabIndHt)) var(--ec-uiPadInl);border:var(--ec-brdWd) solid var(--ec-frm-edActTabBrdCol);border-radius:var(--tab-border-radius) var(--tab-border-radius) 0 0;border-bottom:none;overflow:hidden}.expressive-code .frame.has-title:not(.is-terminal) .title::after{content:'';position:absolute;pointer-events:none;inset:0;border-top:var(--ec-frm-edActTabIndHt) solid var(--ec-frm-edActTabIndTopCol);border-bottom:var(--ec-frm-edActTabIndHt) solid var(--ec-frm-edActTabIndBtmCol)}.expressive-code .frame.has-title:not(.is-terminal) .header{display:flex;background:linear-gradient(to top, var(--ec-frm-edTabBarBrdBtmCol) var(--ec-brdWd), transparent var(--ec-brdWd)),linear-gradient(var(--ec-frm-edTabBarBg), var(--ec-frm-edTabBarBg));background-repeat:no-repeat;padding-inline-start:var(--ec-frm-edTabsMargInlStart)}.expressive-code .frame.has-title:not(.is-terminal) .header::before{content:'';position:absolute;pointer-events:none;inset:0;border:var(--ec-brdWd) solid var(--ec-frm-edTabBarBrdCol);border-radius:inherit;border-bottom:none}.expressive-code .frame.is-terminal{--button-spacing:calc(1.9rem + var(--ec-brdWd) + 2 * var(--ec-uiPadBlk));--code-background:var(--ec-frm-trmBg)}.expressive-code .frame.is-terminal .header{display:flex;align-items:center;justify-content:center;padding-block:var(--ec-uiPadBlk);padding-block-end:calc(var(--ec-uiPadBlk) + var(--ec-brdWd));position:relative;font-weight:500;letter-spacing:0.025ch;color:var(--ec-frm-trmTtbFg);background:var(--ec-frm-trmTtbBg);border:var(--ec-brdWd) solid var(--ec-brdCol);border-bottom:none}.expressive-code .frame.is-terminal .header::before{content:'';position:absolute;pointer-events:none;left:var(--ec-uiPadInl);width:2.1rem;height:0.56rem;line-height:0;background-color:var(--ec-frm-trmTtbDotsFg);opacity:var(--ec-frm-trmTtbDotsOpa);-webkit-mask-image:var(--ec-frm-trmIcon);-webkit-mask-repeat:no-repeat;mask-image:var(--ec-frm-trmIcon);mask-repeat:no-repeat}.expressive-code .frame.is-terminal .header::after{content:'';position:absolute;pointer-events:none;inset:0;border-bottom:var(--ec-brdWd) solid var(--ec-frm-trmTtbBrdBtmCol)}.expressive-code .frame pre{background:var(--code-background)}.expressive-code .copy{display:flex;gap:0.25rem;flex-direction:row;position:absolute;inset-block-start:calc(var(--ec-brdWd) + var(--button-spacing));inset-inline-end:calc(var(--ec-brdWd) + var(--ec-uiPadInl) / 2)}@media (scripting: none){.expressive-code .copy{display:none}}.expressive-code .copy{direction:ltr;unicode-bidi:isolate}.expressive-code .copy button{position:relative;align-self:flex-end;margin:0;padding:0;border:none;border-radius:0.2rem;z-index:1;cursor:pointer;transition-property:opacity, background, border-color;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.25, 0.46, 0.45, 0.94);width:2.5rem;height:2.5rem;background:var(--code-background);opacity:0.75}.expressive-code .copy button div{position:absolute;inset:0;border-radius:inherit;background:var(--ec-frm-inlBtnBg);opacity:var(--ec-frm-inlBtnBgIdleOpa);transition-property:inherit;transition-duration:inherit;transition-timing-function:inherit}.expressive-code .copy button::before{content:'';position:absolute;pointer-events:none;inset:0;border-radius:inherit;border:var(--ec-brdWd) solid var(--ec-frm-inlBtnBrd);opacity:var(--ec-frm-inlBtnBrdOpa)}.expressive-code .copy button::after{content:'';position:absolute;pointer-events:none;inset:0;background-color:var(--ec-frm-inlBtnFg);-webkit-mask-image:var(--ec-frm-copyIcon);-webkit-mask-repeat:no-repeat;mask-image:var(--ec-frm-copyIcon);mask-repeat:no-repeat;margin:0.475rem;line-height:0}.expressive-code .copy button:hover,.expressive-code .copy button:focus:focus-visible{opacity:1}.expressive-code .copy button:hover div,.expressive-code .copy button:focus:focus-visible div{opacity:var(--ec-frm-inlBtnBgHoverOrFocusOpa)}.expressive-code .copy button:active{opacity:1}.expressive-code .copy button:active div{opacity:var(--ec-frm-inlBtnBgActOpa)}.expressive-code .copy .feedback{--tooltip-arrow-size:0.35rem;--tooltip-bg:var(--ec-frm-tooltipSuccessBg);color:var(--ec-frm-tooltipSuccessFg);pointer-events:none;user-select:none;-webkit-user-select:none;position:relative;align-self:center;background-color:var(--tooltip-bg);z-index:99;padding:0.125rem 0.75rem;border-radius:0.2rem;margin-inline-end:var(--tooltip-arrow-size);opacity:0;transition-property:opacity, transform;transition-duration:0.2s;transition-timing-function:ease-in-out;transform:translate3d(0, 0.25rem, 0)}.expressive-code .copy .feedback::after{content:'';position:absolute;pointer-events:none;top:calc(50% - var(--tooltip-arrow-size));inset-inline-end:calc(-2 * (var(--tooltip-arrow-size) - 0.5px));border:var(--tooltip-arrow-size) solid transparent;border-inline-start-color:var(--tooltip-bg)}.expressive-code .copy .feedback.show{opacity:1;transform:translate3d(0, 0, 0)}@media (hover: hover){.expressive-code{}.expressive-code .copy button{opacity:0;width:2rem;height:2rem}.expressive-code .frame:hover .copy button:not(:hover),.expressive-code .frame:focus-within :focus-visible ~ .copy button:not(:hover),.expressive-code .frame .copy .feedback.show ~ button:not(:hover){opacity:0.75}}.expressive-code :nth-child(1 of .ec-line) .code{padding-inline-end:calc(2rem + var(--ec-codePadInl))}.expressive-code .ec-section{position:relative}.expressive-code .ec-section summary{position:relative;font-family:var(--ec-cs-closedFontFml);font-size:var(--ec-cs-closedFontSize);line-height:var(--ec-cs-closedLineHt);user-select:none;-webkit-user-select:none;cursor:pointer;color:var(--ec-cs-closedTextCol);background-color:var(--ec-cs-closedBgCol);--border-color:var(--ec-cs-closedBrdCol);--border-width:var(--ec-cs-closedBrdWd);box-shadow:inset 0 calc(-1 * var(--border-width)) var(--border-color), inset 0 var(--border-width) var(--border-color);margin:var(--ec-cs-closedMarg);padding:0}.expressive-code .ec-section summary::marker{display:inline-block;content:"";width:16px;height:16px}.expressive-code .ec-section summary::-webkit-details-marker{display:none}.expressive-code .ec-section summary :is(.expand, .collapse){position:relative;display:inline-block;width:16px;height:16px;vertical-align:text-bottom;opacity:0.75}.expressive-code .ec-section summary :is(.expand, .collapse)::after{content:'';position:absolute;pointer-events:none;inset:0;background-color:var(--ec-cs-closedTextCol);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;line-height:0}.expressive-code .ec-section summary .expand::after{-webkit-mask-image:var(--ec-cs-expandIcon);mask-image:var(--ec-cs-expandIcon);-webkit-print-color-adjust:exact;print-color-adjust:exact}.expressive-code .ec-section summary .collapse{display:none}.expressive-code .ec-section summary .collapse::after{-webkit-mask-image:var(--ec-cs-collapseIcon);mask-image:var(--ec-cs-collapseIcon)}.expressive-code .ec-section summary .text{margin-left:1em}.expressive-code .ec-section summary .ec-line .code{padding-block:var(--ec-cs-closedPadBlk);text-indent:0}.expressive-code .ec-section[open],.expressive-code .ec-section details[open] + .content-lines{--border-color:var(--ec-cs-openBrdCol);--border-width:var(--ec-cs-openBrdWd);box-shadow:inset 0 calc(-1 * var(--border-width)) var(--border-color), inset 0 var(--border-width) var(--border-color);padding-inline:var(--ec-cs-openPad);margin-inline:var(--ec-cs-openMarg)}.expressive-code .ec-section.github[open] summary{display:none}.expressive-code .ec-section.github[open]{background-color:var(--ec-cs-openBgCol)}.expressive-code .ec-section:is(.collapsible-start, .collapsible-end){display:flex;flex-direction:column}.expressive-code .ec-section:is(.collapsible-start, .collapsible-end) .content-lines{display:none}.expressive-code .ec-section:is(.collapsible-start, .collapsible-end) details[open] .collapse{display:inline-block}.expressive-code .ec-section:is(.collapsible-start, .collapsible-end) details[open] :is(.expand, .text){display:none}.expressive-code .ec-section:is(.collapsible-start, .collapsible-end) details[open] + .content-lines{display:block;background-color:var(--ec-cs-openBgColCollapsible)}@media print{.expressive-code .ec-section:is(.collapsible-start, .collapsible-end) details[open]{display:none}}.expressive-code .ec-section.collapsible-end{flex-direction:column-reverse}.expressive-code .gutter .ln{display:inline-flex;justify-content:flex-end;align-items:flex-start;box-sizing:content-box;min-width:var(--lnWidth, 2ch);padding-inline:2ch;color:var(--ec-lineNumbers-fg)}.highlight .expressive-code .gutter .ln{color:var(--ec-lineNumbers-hlFg)}:root,:root:not([data-theme="dark"]) .expressive-code[data-theme="dark"]{--ec-brdRad:0.3rem;--ec-brdWd:1.5px;--ec-brdCol:var(--border);--ec-codeFontFml:var(--font-mono);--ec-codeFontSize:0.75rem;--ec-codeFontWg:400;--ec-codeLineHt:1.65;--ec-codePadBlk:1rem;--ec-codePadInl:1.35rem;--ec-codeBg:color-mix(in oklab, var(--muted) 25%, transparent);--ec-codeFg:#e1e4e8;--ec-codeSelBg:#3392ff44;--ec-gtrFg:#444d56;--ec-gtrBrdCol:#444d5633;--ec-gtrBrdWd:1.5px;--ec-gtrHlFg:#e1e4e8;--ec-uiFontFml:var(--font-sans);--ec-uiFontSize:0.9rem;--ec-uiFontWg:400;--ec-uiLineHt:1.65;--ec-uiPadBlk:0.25rem;--ec-uiPadInl:1rem;--ec-uiSelBg:#39414a;--ec-uiSelFg:#e1e4e8;--ec-focusBrd:#005cc5;--ec-sbThumbCol:#6a737d33;--ec-sbThumbHoverCol:#6a737d44;--ec-tm-lineMarkerAccentMarg:0rem;--ec-tm-lineMarkerAccentWd:0.15rem;--ec-tm-lineMarkerLabelPadInl:0.2rem;--ec-tm-lineMarkerLabelCol:white;--ec-tm-lineDiffIndMargLeft:0.3rem;--ec-tm-inlMarkerBrdWd:1.5px;--ec-tm-inlMarkerBrdRad:0.2rem;--ec-tm-inlMarkerPad:0.15rem;--ec-tm-insDiffIndContent:'+';--ec-tm-delDiffIndContent:'-';--ec-tm-markBg:#264a8980;--ec-tm-markBrdCol:#5570b3d0;--ec-tm-insBg:#26561c80;--ec-tm-insBrdCol:#4e7e41d0;--ec-tm-insDiffIndCol:#7eb070d0;--ec-tm-delBg:#81322b80;--ec-tm-delBrdCol:#ae594fd0;--ec-tm-delDiffIndCol:#e68a7ed0;--ec-frm-shdCol:#0000005b;--ec-frm-frameBoxShdCssVal:none;--ec-frm-edActTabBg:color-mix(in oklab, var(--muted) 25%, transparent);--ec-frm-edActTabFg:var(--muted-foreground);--ec-frm-edActTabBrdCol:transparent;--ec-frm-edActTabIndHt:1.5px;--ec-frm-edActTabIndTopCol:transparent;--ec-frm-edActTabIndBtmCol:transparent;--ec-frm-edTabsMargInlStart:0;--ec-frm-edTabsMargBlkStart:0;--ec-frm-edTabBrdRad:0px;--ec-frm-edTabBarBg:transparent;--ec-frm-edTabBarBrdCol:var(--border);--ec-frm-edTabBarBrdBtmCol:transparent;--ec-frm-edBg:color-mix(in oklab, var(--muted) 25%, transparent);--ec-frm-trmTtbFg:var(--muted-foreground);--ec-frm-trmTtbDotsFg:var(--muted-foreground);--ec-frm-trmTtbDotsOpa:0.15;--ec-frm-trmTtbBg:transparent;--ec-frm-trmTtbBrdBtmCol:transparent;--ec-frm-trmBg:color-mix(in oklab, var(--muted) 25%, transparent);--ec-frm-inlBtnFg:#e1e4e8;--ec-frm-inlBtnBg:#e1e4e8;--ec-frm-inlBtnBgIdleOpa:0;--ec-frm-inlBtnBgHoverOrFocusOpa:0.2;--ec-frm-inlBtnBgActOpa:0.3;--ec-frm-inlBtnBrd:#e1e4e8;--ec-frm-inlBtnBrdOpa:0.4;--ec-frm-tooltipSuccessBg:#228739;--ec-frm-tooltipSuccessFg:white;--ec-frm-copyIcon:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2024%2024'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.75'%3E%3Cpath%20d%3D'M3%2019a2%202%200%200%201-1-2V2a2%202%200%200%201%201-1h13a2%202%200%200%201%202%201'%2F%3E%3Crect%20x%3D'6'%20y%3D'5'%20width%3D'16'%20height%3D'18'%20rx%3D'1.5'%20ry%3D'1.5'%2F%3E%3C%2Fsvg%3E");--ec-frm-trmIcon:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2060%2016'%20preserveAspectRatio%3D'xMidYMid%20meet'%3E%3Ccircle%20cx%3D'8'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'30'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'52'%20cy%3D'8'%20r%3D'8'%2F%3E%3C%2Fsvg%3E");--ec-cs-closedBrdWd:0px;--ec-cs-closedPadBlk:4px;--ec-cs-closedMarg:0;--ec-cs-closedFontFml:inherit;--ec-cs-closedFontSize:inherit;--ec-cs-closedLineHt:inherit;--ec-cs-closedTextCol:#e1e4e8;--ec-cs-closedBgCol:#58606933;--ec-cs-closedBrdCol:#58606980;--ec-cs-openBrdWd:1px;--ec-cs-openPad:0;--ec-cs-openMarg:0;--ec-cs-openBgCol:transparent;--ec-cs-openBgColCollapsible:#5860691a;--ec-cs-openBrdCol:transparent;--ec-cs-expandIcon:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%3E%3Cpath%20d%3D'm8.177.677%202.896%202.896a.25.25%200%200%201-.177.427H8.75v1.25a.75.75%200%200%201-1.5%200V4H5.104a.25.25%200%200%201-.177-.427L7.823.677a.25.25%200%200%201%20.354%200ZM7.25%2010.75a.75.75%200%200%201%201.5%200V12h2.146a.25.25%200%200%201%20.177.427l-2.896%202.896a.25.25%200%200%201-.354%200l-2.896-2.896A.25.25%200%200%201%205.104%2012H7.25v-1.25Zm-5-2a.75.75%200%200%200%200-1.5h-.5a.75.75%200%200%200%200%201.5h.5ZM6%208a.75.75%200%200%201-.75.75h-.5a.75.75%200%200%201%200-1.5h.5A.75.75%200%200%201%206%208Zm2.25.75a.75.75%200%200%200%200-1.5h-.5a.75.75%200%200%200%200%201.5h.5ZM12%208a.75.75%200%200%201-.75.75h-.5a.75.75%200%200%201%200-1.5h.5A.75.75%200%200%201%2012%208Zm2.25.75a.75.75%200%200%200%200-1.5h-.5a.75.75%200%200%200%200%201.5h.5Z'%2F%3E%3C%2Fsvg%3E");--ec-cs-collapseIcon:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%3E%3Cpath%20d%3D'M10.896%202H8.75V.75a.75.75%200%200%200-1.5%200V2H5.104a.25.25%200%200%200-.177.427l2.896%202.896a.25.25%200%200%200%20.354%200l2.896-2.896A.25.25%200%200%200%2010.896%202ZM8.75%2015.25a.75.75%200%200%201-1.5%200V14H5.104a.25.25%200%200%201-.177-.427l2.896-2.896a.25.25%200%200%201%20.354%200l2.896%202.896a.25.25%200%200%201-.177.427H8.75v1.25Zm-6.5-6.5a.75.75%200%200%200%200-1.5h-.5a.75.75%200%200%200%200%201.5h.5ZM6%208a.75.75%200%200%201-.75.75h-.5a.75.75%200%200%201%200-1.5h.5A.75.75%200%200%201%206%208Zm2.25.75a.75.75%200%200%200%200-1.5h-.5a.75.75%200%200%200%200%201.5h.5ZM12%208a.75.75%200%200%201-.75.75h-.5a.75.75%200%200%201%200-1.5h.5A.75.75%200%200%201%2012%208Zm2.25.75a.75.75%200%200%200%200-1.5h-.5a.75.75%200%200%200%200%201.5h.5Z'%2F%3E%3C%2Fsvg%3E");--ec-lineNumbers-fg:var(--muted-foreground);--ec-lineNumbers-hlFg:inherit}.expressive-code .ec-line :where(span[style^='--']:not([class])),:root:not([data-theme="dark"]) .expressive-code[data-theme="dark"] .ec-line :where(span[style^='--']:not([class])){color:var(--0, inherit);background-color:var(--0bg, transparent);font-style:var(--0fs, inherit);font-weight:var(--0fw, inherit);text-decoration:var(--0td, inherit)}:root[data-theme="light"] .expressive-code:not([data-theme="dark"]),.expressive-code[data-theme="light"]{--ec-codeFg:#24292e;--ec-codeSelBg:#0366d625;--ec-gtrFg:#1b1f234d;--ec-gtrBrdCol:#1b1f2333;--ec-gtrHlFg:#24292e;--ec-uiSelBg:#e2e5e9;--ec-uiSelFg:#2f363d;--ec-focusBrd:#2188ff;--ec-sbThumbCol:#959da533;--ec-sbThumbHoverCol:#959da544;--ec-tm-markBg:#9fb6ff80;--ec-tm-insBg:#94c68480;--ec-tm-insDiffIndCol:#3a692fd0;--ec-tm-delBg:#fea09280;--ec-tm-delDiffIndCol:#97453dd0;--ec-frm-shdCol:#00000028;--ec-frm-inlBtnFg:#24292e;--ec-frm-inlBtnBg:#24292e;--ec-frm-inlBtnBrd:#24292e;--ec-frm-tooltipSuccessBg:#208638;--ec-cs-closedTextCol:#24292e;--ec-cs-closedBgCol:#d1d5da33;--ec-cs-closedBrdCol:#d1d5da80;--ec-cs-openBgColCollapsible:#d1d5da1a}:root[data-theme="light"] .expressive-code:not([data-theme="dark"]) .ec-line :where(span[style^='--']:not([class])),.expressive-code[data-theme="light"] .ec-line :where(span[style^='--']:not([class])){color:var(--1, inherit);background-color:var(--1bg, transparent);font-style:var(--1fs, inherit);font-weight:var(--1fw, inherit);text-decoration:var(--1td, inherit)}</style><script type="module">try{(()=>{function a(e){if(!e)return;let t=e.getAttribute("tabindex")!==null,r=e.scrollWidth>e.clientWidth;r&&!t?(e.setAttribute("tabindex","0"),e.setAttribute("role","region")):!r&&t&&(e.removeAttribute("tabindex"),e.removeAttribute("role"))}var u=window.requestIdleCallback||(e=>setTimeout(e,1)),s=window.cancelIdleCallback||clearTimeout;function l(e){let t=new Set,r,n;return new ResizeObserver(c=>{c.forEach(o=>t.add(o.target)),r&&clearTimeout(r),n&&s(n),r=setTimeout(()=>{n&&s(n),n=u(()=>{t.forEach(o=>e(o)),t.clear()})},250)})}function i(e,t){e.querySelectorAll?.(".expressive-code pre > code").forEach(r=>{let n=r.parentElement;n&&t.observe(n)})}var d=l(a);i(document,d);var b=new MutationObserver(e=>e.forEach(t=>t.addedNodes.forEach(r=>{i(r,d)})));b.observe(document.body,{childList:!0,subtree:!0});document.addEventListener("astro:page-load",()=>{i(document,d)});})();}catch(e){console.error("[EC] tabindex-js-module failed:",e)}</script><script type="module">try{(()=>{function l(o){let e=document.createElement("pre");Object.assign(e.style,{opacity:"0",pointerEvents:"none",position:"absolute",overflow:"hidden",left:"0",top:"0",width:"20px",height:"20px",webkitUserSelect:"auto",userSelect:"all"}),e.ariaHidden="true",e.textContent=o,document.body.appendChild(e);let a=document.createRange();a.selectNode(e);let n=getSelection();if(!n)return!1;n.removeAllRanges(),n.addRange(a);let r=!1;try{r=document.execCommand("copy")}finally{n.removeAllRanges(),document.body.removeChild(e)}return r}async function u(o){let e=o.currentTarget,a=e.dataset,n=!1,r=a.code.replace(/\u007f/g,`
`);try{await navigator.clipboard.writeText(r),n=!0}catch{n=l(r)}if(!n||e.parentNode?.querySelector(".feedback"))return;let c=e.parentNode?.querySelector("[aria-live]"),t=document.createElement("div");t.classList.add("feedback"),t.append(a.copied),c.append(t),t.offsetWidth,requestAnimationFrame(()=>t?.classList.add("show"));let s=()=>!t||t.classList.remove("show"),d=()=>{!t||parseFloat(getComputedStyle(t).opacity)>0||(t.remove(),t=void 0)};setTimeout(s,1500),setTimeout(d,2500),e.addEventListener("blur",s),t.addEventListener("transitioncancel",d),t.addEventListener("transitionend",d)}function i(o){o.querySelectorAll?.(".expressive-code .copy button").forEach(e=>e.addEventListener("click",u))}i(document);var m=new MutationObserver(o=>o.forEach(e=>e.addedNodes.forEach(a=>{i(a)})));m.observe(document.body,{childList:!0,subtree:!0});document.addEventListener("astro:page-load",()=>{i(document)});})();}catch(e){console.error("[EC] copy-js-module failed:",e)}</script><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:93ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Dec</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">6</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">23</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">17</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">01</span><span style="--0:#E1E4E8;--1:#24292E"> logstash </span><span style="--0:#79B8FF;--1:#005CC5">CRON</span><span style="--0:#E1E4E8;--1:#24292E">[</span><span style="--0:#79B8FF;--1:#005CC5">5849</span><span style="--0:#E1E4E8;--1:#24292E">]: (root) </span><span style="--0:#79B8FF;--1:#005CC5">CMD</span><span style="--0:#E1E4E8;--1:#24292E"> (cd </span><span style="--0:#F97583;--1:#BF3441">/</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">&#x26;&#x26;</span><span style="--0:#E1E4E8;--1:#24292E"> run</span><span style="--0:#F97583;--1:#BF3441">-</span><span style="--0:#E1E4E8;--1:#24292E">parts </span><span style="--0:#F97583;--1:#BF3441">--</span><span style="--0:#E1E4E8;--1:#24292E">report </span><span style="--0:#F97583;--1:#BF3441">/</span><span style="--0:#E1E4E8;--1:#24292E">etc</span><span style="--0:#F97583;--1:#BF3441">/</span><span style="--0:#E1E4E8;--1:#24292E">cron.</span><span style="--0:#B392F0;--1:#6F42C1">hourly</span><span style="--0:#E1E4E8;--1:#24292E">)</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="Dec 6 23:17:01 logstash CRON[5849]: (root) CMD (cd / &#x26;&#x26; run-parts --report /etc/cron.hourly)"><div></div></button></div></figure></div>
<p>In addition to the payload, in this case a report on a Cron job, it has a date stamp and a source (the host <code>logstash</code>). Whilst versatile and readable, the Syslog format is not ideal as it’s basically one long string. This string is awesome from a human readability perspective. It’s very easy to glance at a Syslog string and know what’s happened. But are you the target audience of a string-based message? Probably back in the day when you had a small volume of hosts and you were connecting to them to read the logs you were the audience. Now you have a pool of hosts, services and applications and your log entries are centralized. That means between you, the human audience, there is now a machine that first consumes the log message before you see it. And because of the eminently readable string format that consumption is not easy.</p>
<p>That format means we’re likely to be forced to resort to regular expressions to parse it. Additionally, probably more than one regular expression. Again Syslog is a good example. <a href="http://kartar.net/2014/09/when-logstash-and-syslog-go-wrong/" rel="nofollow noreferrer noopener" target="_blank">Implementations across platforms are sometimes subtly different and this often means more than one regular expression needs to be implemented and then maintained</a>. This additional overhead means it’s much harder to extract the value, diagnostic or operational, from our log data.</p>
<p>There is, however, a better way of generating logs: structured logs (also known as semantic or typed logs). There’s no standard currently for structured logging. There are some examples of attempts to formalize a structured logging formats like the <a href="https://cee.mitre.org/" rel="nofollow noreferrer noopener" target="_blank">Common Event Expression</a> and <a href="https://fedorahosted.org/lumberjack/" rel="nofollow noreferrer noopener" target="_blank">Project Lumberjack</a>. None of them got much traction and are largely unmaintained. But we can still describe the concept of structured logging. Instead of a string, like our Syslog examples, structured logs try to preserve typed, rich data rather than convert it. Let’s look at an example of some code that produces an unstructured string:</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:72ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Logger</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">error</span><span style="--0:#E1E4E8;--1:#24292E">(</span><span style="--0:#9ECBFF;--1:#032F62">"The system had a hiccup trying to create user"</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">+</span><span style="--0:#E1E4E8;--1:#24292E"> username)</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="Logger.error(&#x22;The system had a hiccup trying to create user&#x22; + username)"><div></div></button></div></figure></div>
<p>Let’s assume the user being created was <code>james@example.com</code>. This pseudo-code would generate a message like:</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:64ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">The</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">system</span><span style="--0:#E1E4E8;--1:#24292E"> had a hiccup trying to create user james@example.</span><span style="--0:#B392F0;--1:#6F42C1">com</span><span style="--0:#E1E4E8;--1:#24292E">.</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="The system had a hiccup trying to create user james@example.com."><div></div></button></div></figure></div>
<p>We’d then have to send that message somewhere, to Logstash for example, and parse it into a useful form.</p>
<p>Alternatively, we can create a more structured message.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:57ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Logger</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">error</span><span style="--0:#E1E4E8;--1:#24292E">(event</span><span style="--0:#F97583;--1:#BF3441">=</span><span style="--0:#9ECBFF;--1:#032F62">"user_creation_failed"</span><span style="--0:#E1E4E8;--1:#24292E">, user</span><span style="--0:#F97583;--1:#BF3441">=</span><span style="--0:#E1E4E8;--1:#24292E">username)</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="Logger.error(event=&#x22;user_creation_failed&#x22;, user=username)"><div></div></button></div></figure></div>
<p>You can see in our structured message that we’ve gotten a head start on any parsing. Assuming we send the log message in some encoded format, JSON for example or a binary format like protocol buffers, then we get an <code>event</code> name, <code>user_creation_failed</code>, and a variable <code>user</code> which contains the user name of the user that we failed to create (or even a user object containing all the parameters of the user being created).</p>
<p>Let’s look at what our JSON encoded event might look like:</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="json" class="wrap" style="--ecMaxLine:35ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">[</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">{</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"time"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">1449454008</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"priority"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"error"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"event"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"user_creation_failed"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">6</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"user"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"james@example.com"</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">7</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">}</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">8</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">]</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="[ { &#x22;time&#x22;:1449454008, &#x22;priority&#x22;:&#x22;error&#x22;, &#x22;event&#x22;:&#x22;user_creation_failed&#x22;, &#x22;user&#x22;:&#x22;james@example.com&#x22; }]"><div></div></button></div></figure></div>
<p>We can see instead of a string we’ve got a JSON array containing a structured log entry: a time, a priority, an event identifier, and some rich data from that event, the user which our application failed to create. We’re now logging a series of objects which are easily consumed by a machine rather than a string we need to parse.</p>
<p>Let’s extend an example Rails application with some structured log events. This is a simple Ruby on Rails application that allows us to create and delete users and not much else. We’re going to add two structured logging libraries, the first called <a href="https://github.com/roidrage/lograge" rel="nofollow noreferrer noopener" target="_blank">Lograge</a> and the second called <a href="https://github.com/dwbutler/logstash-logger" rel="nofollow noreferrer noopener" target="_blank">Logstash-logger</a> to our application. The Lograge library formats Rails-style request logs into a structured format, by default JSON, but can also generate Logstash-structured events. The second library, Logstash-logger, allows us to hijack Rails existing logging framework and emit much more structured events and then send them directly to Logstash. Let’s install them now and then see what some structured logging messages might look like.</p>
<p>We first need to add three gems, <code>lograge</code>, <code>logstash-event</code> and <code>logstash-logger</code>, to our application to enable our structured logging support.</p>
<p>The <code>lograge</code> gem enables Lograge’s request log re-formatting. The <code>logstash-event</code> gem allows Lograge to format requests into Logstash events. The <code>logstash-logger</code> gem allows you to output log events in Logstash’s event format and enables a variety of potential logging destinations, including Logstash. We’re going to start by adding the gems to our Rails application’s <code>Gemfile</code>.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:29ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">source </span><span style="--0:#9ECBFF;--1:#032F62">'https://rubygems.org'</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">ruby </span><span style="--0:#9ECBFF;--1:#032F62">'2.2.2'</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">gem </span><span style="--0:#9ECBFF;--1:#032F62">'rails'</span><span style="--0:#E1E4E8;--1:#24292E">, </span><span style="--0:#9ECBFF;--1:#032F62">'4.2.4'</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">. . .</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">gem </span><span style="--0:#9ECBFF;--1:#032F62">'lograge'</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">6</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">gem </span><span style="--0:#9ECBFF;--1:#032F62">'logstash-event'</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">7</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">gem </span><span style="--0:#9ECBFF;--1:#032F62">'logstash-logger'</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">8</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">. . .</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="source &#x27;https://rubygems.org&#x27;ruby &#x27;2.2.2&#x27;gem &#x27;rails&#x27;, &#x27;4.2.4&#x27;. . .gem &#x27;lograge&#x27;gem &#x27;logstash-event&#x27;gem &#x27;logstash-logger&#x27;. . ."><div></div></button></div></figure></div>
<p>We then install the new gems using the <code>bundle</code> command.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:59ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">$ sudo bundle install</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Fetching</span><span style="--0:#E1E4E8;--1:#24292E"> gem metadata from </span><span style="--0:#79B8FF;--1:#005CC5">https:</span><span style="--0:#F97583;--1:#BF3441">/</span><span style="--0:#DBEDFF;--1:#032F62">/rubygems.org/</span><span style="--0:#E1E4E8;--1:#24292E">...........</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Fetching</span><span style="--0:#E1E4E8;--1:#24292E"> version metadata from </span><span style="--0:#79B8FF;--1:#005CC5">https:</span><span style="--0:#F97583;--1:#BF3441">/</span><span style="--0:#DBEDFF;--1:#032F62">/rubygems.org/</span><span style="--0:#E1E4E8;--1:#24292E">...</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Fetching</span><span style="--0:#E1E4E8;--1:#24292E"> dependency metadata from </span><span style="--0:#79B8FF;--1:#005CC5">https:</span><span style="--0:#F97583;--1:#BF3441">/</span><span style="--0:#DBEDFF;--1:#032F62">/rubygems.org/</span><span style="--0:#E1E4E8;--1:#24292E">..</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">. . .</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">6</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Installing</span><span style="--0:#E1E4E8;--1:#24292E"> lograge</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">7</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Installing</span><span style="--0:#E1E4E8;--1:#24292E"> logstash</span><span style="--0:#F97583;--1:#BF3441">-</span><span style="--0:#E1E4E8;--1:#24292E">event</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">8</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Installing</span><span style="--0:#E1E4E8;--1:#24292E"> logstash</span><span style="--0:#F97583;--1:#BF3441">-</span><span style="--0:#E1E4E8;--1:#24292E">logger</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">9</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">. . .</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="$ sudo bundle installFetching gem metadata from https://rubygems.org/...........Fetching version metadata from https://rubygems.org/...Fetching dependency metadata from https://rubygems.org/... . .Installing logrageInstalling logstash-eventInstalling logstash-logger. . ."><div></div></button></div></figure></div>
<p>Next, we need to enable all of our new logging components inside our Rails application’s configuration. We’re only going to enable each component for the <code>production</code> environment. To do this we add our configuration to the <code>config/environments/production.rb</code> file.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:90ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Rails</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">application</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">configure</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">do</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span class="indent"> </span><span style="--0:#99A0A6;--1:#616972"># Settings specified here will take precedence over those in config/application.rb.</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code">
</div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">. . .</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">config.</span><span style="--0:#B392F0;--1:#6F42C1">log_level</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">=</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">:info</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">6</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">config.</span><span style="--0:#B392F0;--1:#6F42C1">lograge</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">enabled</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">=</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">true</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">7</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">config.</span><span style="--0:#B392F0;--1:#6F42C1">lograge</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">formatter</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">=</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">Lograge</span><span style="--0:#E1E4E8;--1:#24292E">::</span><span style="--0:#79B8FF;--1:#005CC5">Formatters</span><span style="--0:#E1E4E8;--1:#24292E">::</span><span style="--0:#79B8FF;--1:#005CC5">Logstash</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#F97583;--1:#BF3441">new</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">8</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">config.</span><span style="--0:#B392F0;--1:#6F42C1">logger</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">=</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">LogStashLogger</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#F97583;--1:#BF3441">new</span><span style="--0:#E1E4E8;--1:#24292E">(</span><span style="--0:#79B8FF;--1:#005CC5">type:</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">:tcp</span><span style="--0:#E1E4E8;--1:#24292E">, </span><span style="--0:#79B8FF;--1:#005CC5">host:</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#9ECBFF;--1:#032F62">'logstash.example.com'</span><span style="--0:#E1E4E8;--1:#24292E">, </span><span style="--0:#79B8FF;--1:#005CC5">port:</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">2020</span><span style="--0:#E1E4E8;--1:#24292E">)</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">9</div></div><div class="code"><span style="--0:#F97583;--1:#BF3441">end</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb.. . . config.log_level = :info config.lograge.enabled = true config.lograge.formatter = Lograge::Formatters::Logstash.new config.logger = LogStashLogger.new(type: :tcp, host: &#x27;logstash.example.com&#x27;, port: 2020)end"><div></div></button></div></figure></div>
<p>Here we’ve configured four options. The first, <code>config.log_level</code>, is a Rails logging default for the log level, here we’re telling Rails to only log events of an <code>:info</code> level or higher. By default, Rails logs at a <code>:debug</code> level. We generally don’t need that kind of detail. The second option, <code>config.lograge.enabled</code> turns on Lograge, taking over Rails default logging for requests. The third option, <code>config.lograge.formatter</code>, controls the format those log events are emitted in, here we’re using Logstash’s event format. Lograge has <a href="https://github.com/roidrage/lograge" rel="nofollow noreferrer noopener" target="_blank">a series of other formats</a> available including raw JSON. The last option, <code>config.logger</code>, takes over Rails default logging with Logstash-logger. It creates a new instance of the <code>LogStashLogger</code> class that connects to our Logstash server, <code>logstash.example.com</code> via TCP on port <code>2020</code>.</p>
<p>Let’s look at the corresponding required configuration on our Logstash server. We need to add a new <code>tcp</code> input to receive our application events.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:19ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">input {</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">tcp {</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">port => </span><span style="--0:#79B8FF;--1:#005CC5">2020</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">type => </span><span style="--0:#9ECBFF;--1:#032F62">"apps"</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">codec => </span><span style="--0:#9ECBFF;--1:#032F62">"json"</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">6</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">}</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">7</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">. . .</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="input { tcp { port => 2020 type => &#x22;apps&#x22; codec => &#x22;json&#x22; }. . ."><div></div></button></div></figure></div>
<p>We can see we’ve added a <code>tcp</code> input running on port <code>2020</code>. We set a <code>type</code> of <code>apps</code> for any events received on this input and we use the <code>json</code> codec to parse any incoming events into Logstash’s message format from JSON. To enable our configuration we would need to restart Logstash.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:31ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">$ sudo service logstash restart</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="$ sudo service logstash restart"><div></div></button></div></figure></div>
<p>So what does this do for our sample application? Well enabling Lograge will convert Rails default request logs into something a lot more structured and a lot more useful. A traditional request log might look like:</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:62ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Started</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">GET</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#9ECBFF;--1:#032F62">"/"</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">for</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">127.0</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#79B8FF;--1:#005CC5">0.1</span><span style="--0:#E1E4E8;--1:#24292E"> at </span><span style="--0:#79B8FF;--1:#005CC5">2015</span><span style="--0:#F97583;--1:#BF3441">-</span><span style="--0:#79B8FF;--1:#005CC5">12</span><span style="--0:#F97583;--1:#BF3441">-</span><span style="--0:#79B8FF;--1:#005CC5">10</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">09</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">21</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">45</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">+</span><span style="--0:#79B8FF;--1:#005CC5">0400</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Processing</span><span style="--0:#E1E4E8;--1:#24292E"> by </span><span style="--0:#79B8FF;--1:#005CC5">UsersController</span><span style="--0:#99A0A6;--1:#616972">#index as HTML</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">Rendered</span><span style="--0:#E1E4E8;--1:#24292E"> users</span><span style="--0:#F97583;--1:#BF3441">/</span><span style="--0:#79B8FF;--1:#005CC5">_user</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">html</span><span style="--0:#E1E4E8;--1:#24292E">.</span><span style="--0:#B392F0;--1:#6F42C1">erb</span><span style="--0:#E1E4E8;--1:#24292E"> (</span><span style="--0:#79B8FF;--1:#005CC5">6.</span><span style="--0:#E1E4E8;--1:#24292E">0ms)</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span style="--0:#79B8FF;--1:#005CC5">Completed</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">200</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">OK</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#F97583;--1:#BF3441">in</span><span style="--0:#E1E4E8;--1:#24292E"> 79ms (</span><span style="--0:#79B8FF;--1:#005CC5">Views:</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">78.</span><span style="--0:#E1E4E8;--1:#24292E">8ms </span><span style="--0:#F97583;--1:#BF3441">|</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">ActiveRecord:</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">0.</span><span style="--0:#E1E4E8;--1:#24292E">0ms)</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="Started GET &#x22;/&#x22; for 127.0.0.1 at 2015-12-10 09:21:45 +0400Processing by UsersController#index as HTML Rendered users/_user.html.erb (6.0ms)Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)"><div></div></button></div></figure></div>
<p>With Lograge enabled a log request like this would appear more like:</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="json" class="wrap" style="--ecMaxLine:47ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">{</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"method"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"GET"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"path"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"/users"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"format"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"html"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"controller"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"users"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">6</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"action"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"index"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">7</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"status"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">200</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">8</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"duration"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">189.35</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">9</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"view"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">186.35</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">10</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"db"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">0.92</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">11</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"@timestamp"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"2015-12-11T13:35:47.062+00:00"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">12</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"@version"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"1"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">13</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"message"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"[200] GET /users (users#index)"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">14</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"severity"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"INFO"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">15</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"host"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"app1-web1"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">16</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"type"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"apps"</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">17</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ &#x22;method&#x22;:&#x22;GET&#x22;, &#x22;path&#x22;:&#x22;/users&#x22;, &#x22;format&#x22;:&#x22;html&#x22;, &#x22;controller&#x22;:&#x22;users&#x22;, &#x22;action&#x22;:&#x22;index&#x22;, &#x22;status&#x22;:200, &#x22;duration&#x22;:189.35, &#x22;view&#x22;:186.35, &#x22;db&#x22;:0.92, &#x22;@timestamp&#x22;:&#x22;2015-12-11T13:35:47.062+00:00&#x22;, &#x22;@version&#x22;:&#x22;1&#x22;, &#x22;message&#x22;:&#x22;[200] GET /users (users#index)&#x22;, &#x22;severity&#x22;:&#x22;INFO&#x22;, &#x22;host&#x22;:&#x22;app1-web1&#x22;, &#x22;type&#x22;:&#x22;apps&#x22;}"><div></div></button></div></figure></div>
<p>We can see that the log event has been converted in a Logstash event. The original base message is now in the <code>message</code> field and each element of the request has been parsed into a field, for example the request’s method is in the <code>method</code> field and the controller in the <code>controller</code> field. Logstash-logger will then send this structured event to our Logstash server where we can parse it, create metrics from it (you can see we have things like the HTTP status code and timings from the request), or store it in Kibana where you will be able to query it.</p>
<p>We can also <a href="https://github.com/dwbutler/logstash-logger#usage-examples" rel="nofollow noreferrer noopener" target="_blank">send stand-alone log events</a> too using Logstash-logger’s override of Rails default <code>logger</code> method. Let’s specify a message that gets sent when we delete a user.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="ruby" class="wrap" style="--ecMaxLine:54ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#F97583;--1:#BF3441">def</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#B392F0;--1:#6F42C1">destroy</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">@user.</span><span style="--0:#B392F0;--1:#6F42C1">destroy</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">logger.</span><span style="--0:#B392F0;--1:#6F42C1">info</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#79B8FF;--1:#005CC5">message:</span><span style="--0:#E1E4E8;--1:#24292E"> </span><span style="--0:#9ECBFF;--1:#032F62">'user_deleted'</span><span style="--0:#E1E4E8;--1:#24292E">, </span><span style="--0:#79B8FF;--1:#005CC5">user:</span><span style="--0:#E1E4E8;--1:#24292E"> @user</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">redirect_to users_path, </span><span style="--0:#79B8FF;--1:#005CC5">:notice</span><span style="--0:#E1E4E8;--1:#24292E"> => </span><span style="--0:#9ECBFF;--1:#032F62">"User deleted."</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span class="indent"> </span><span style="--0:#F97583;--1:#BF3441">end</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="def destroy @user.destroy logger.info message: &#x27;user_deleted&#x27;, user: @user redirect_to users_path, :notice => &#x22;User deleted.&#x22; end"><div></div></button></div></figure></div>
<p>Here we’ve added a <code>logger.info</code> call to the <code>destroy</code> method. We’ve passed it two arguments, <code>message</code> and <code>user</code>. The <code>message</code> argument will become the value of our <code>message</code> field in the Logstash event. The <code>user</code> field will also become a field containing the <code>@user</code> instance variable, which in turn contains the details of the user being deleted. Let’s look at an event that might be generated when we delete the user <code>james</code>.</p>
<div class="expressive-code"><figure class="frame"><figcaption class="header"></figcaption><pre data-language="json" class="wrap" style="--ecMaxLine:47ch"><code><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">1</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">{</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">2</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"message"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"user_deleted"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">3</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"user"</span><span style="--0:#E1E4E8;--1:#24292E">: {</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">4</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"id"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">6</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">5</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"email"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"james@example.com"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">6</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"created_at"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"2015-12-11T04:31:46.828Z"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">7</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"updated_at"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"2015-12-11T04:32:18.340Z"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">8</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"name"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"james"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">9</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"role"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"user"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">10</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invitation_token"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">null</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">11</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invitation_created_at"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">null</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">12</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invitation_sent_at"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">null</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">13</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invitation_accepted_at"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">null</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">14</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invitation_limit"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">null</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">15</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invited_by_id"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">null</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">16</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invited_by_type"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">null</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:4ch"><div class="gutter"><div class="ln" aria-hidden="true">17</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"invitations_count"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#79B8FF;--1:#005CC5">0</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">18</div></div><div class="code"><span class="indent"><span style="--0:#E1E4E8;--1:#24292E"> </span></span><span style="--0:#E1E4E8;--1:#24292E">},</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">19</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"@timestamp"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"2015-12-11T13:35:50.070+00:00"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">20</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"@version"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"1"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">21</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"severity"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"INFO"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">22</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"host"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"app1-web1"</span><span style="--0:#E1E4E8;--1:#24292E">,</span></div></div><div class="ec-line" style="--ecIndent:2ch"><div class="gutter"><div class="ln" aria-hidden="true">23</div></div><div class="code"><span class="indent"> </span><span style="--0:#79B8FF;--1:#005CC5">"type"</span><span style="--0:#E1E4E8;--1:#24292E">:</span><span style="--0:#9ECBFF;--1:#032F62">"apps"</span></div></div><div class="ec-line"><div class="gutter"><div class="ln" aria-hidden="true">24</div></div><div class="code"><span style="--0:#E1E4E8;--1:#24292E">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="{ &#x22;message&#x22;:&#x22;user_deleted&#x22;, &#x22;user&#x22;: { &#x22;id&#x22;:6, &#x22;email&#x22;:&#x22;james@example.com&#x22;, &#x22;created_at&#x22;:&#x22;2015-12-11T04:31:46.828Z&#x22;, &#x22;updated_at&#x22;:&#x22;2015-12-11T04:32:18.340Z&#x22;, &#x22;name&#x22;:&#x22;james&#x22;, &#x22;role&#x22;:&#x22;user&#x22;, &#x22;invitation_token&#x22;:null, &#x22;invitation_created_at&#x22;:null, &#x22;invitation_sent_at&#x22;:null, &#x22;invitation_accepted_at&#x22;:null, &#x22;invitation_limit&#x22;:null, &#x22;invited_by_id&#x22;:null, &#x22;invited_by_type&#x22;:null, &#x22;invitations_count&#x22;:0 }, &#x22;@timestamp&#x22;:&#x22;2015-12-11T13:35:50.070+00:00&#x22;, &#x22;@version&#x22;:&#x22;1&#x22;, &#x22;severity&#x22;:&#x22;INFO&#x22;, &#x22;host&#x22;:&#x22;app1-web1&#x22;, &#x22;type&#x22;:&#x22;apps&#x22;}"><div></div></button></div></figure></div>
<p>We can see our event is in Logstash format with our <code>user_deleted</code> message and the contents of the <code>@user</code> instance variable structured as fields of a <code>user</code> hash. When a user is deleted this event would be passed to Logstash and could then be processed and stored. Once there you can see there are more than enough details to help us diagnose issues and track events.</p>
<p>This is a super simple example of how structured logging can make monitoring your applications so much easier. You can apply the basic principles articulated here in a variety of languages and frameworks.</p>
<h2 id="structured-logging-libraries">Structured logging libraries</h2>
<p>Just to get you started, here are some structured logging libraries and integrations for a variety of languages and frameworks. You should be able to find others by searching online.</p>
<h3 id="java">Java</h3>
<p>The Java community has the powerful and venerable <a href="http://logging.apache.org/log4j/" rel="nofollow noreferrer noopener" target="_blank">Log4j</a>. It’s a hugely configurable and flexible.</p>
<h3 id="go">Go</h3>
<p>Golang has <a href="https://github.com/Sirupsen/logrus" rel="nofollow noreferrer noopener" target="_blank">Logrus</a>, which extends the standard logger library with structured data.</p>
<h3 id="clojure">Clojure</h3>
<p>Clojure has a couple of good structured logging implementations. One from <a href="https://github.com/puppetlabs/structured-logging" rel="nofollow noreferrer noopener" target="_blank">Puppet Labs</a> and the other <a href="https://github.com/yogthos/clj-log" rel="nofollow noreferrer noopener" target="_blank">clj-log</a>.</p>
<h3 id="ruby--rails">Ruby &#x26; Rails</h3>
<p>We’ve already seen <a href="https://github.com/roidrage/lograge" rel="nofollow noreferrer noopener" target="_blank">Lograge</a> for Ruby and Rails. Other examples include <a href="https://github.com/rocketjob/semantic_logger" rel="nofollow noreferrer noopener" target="_blank">Semantic Logger</a> and <a href="https://github.com/jordansissel/ruby-cabin" rel="nofollow noreferrer noopener" target="_blank">ruby-cabin</a>.</p>
<h3 id="python">Python</h3>
<p>Python has <a href="https://github.com/hynek/structlog" rel="nofollow noreferrer noopener" target="_blank">Structlog</a> which augments the existing Logger methods with structured data.</p>
<h3 id="javascript--nodejs">Javascript &#x26; Node.JS</h3>
<p>Javascript (and Node) has an implementation of .Net’s Serilog available called <a href="https://github.com/structured-log/structured-log" rel="nofollow noreferrer noopener" target="_blank">Structured Log</a>. Another example is <a href="https://github.com/trentm/node-bunyan" rel="nofollow noreferrer noopener" target="_blank">Bunyan</a>.</p>
<h3 id="net">.Net</h3>
<p>The .Net framework has <a href="http://serilog.net/" rel="nofollow noreferrer noopener" target="_blank">Serilog</a>.</p></article><div class="col-start-2"><div class="share-links"><h3 class="text-base font-bold font-display mb-3">Share this post</h3><div class="flex space-x-2"><a href="https://twitter.com/intent/tweet?text=Structured%20Logging&amp;url=https%3A%2F%2Fkartar.net%2F2015%2F12%2Fstructured-logging%2F" target="_blank" rel="noopener noreferrer" class="p-2 rounded-sm bg-card border text-muted-foreground hover:text-primary hover:border-primary transition-colors" aria-label="Share on Twitter" title="Share on Twitter"><svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M22 4.01c-1 .49 -1.98 .689 -3 .99c-1.121 -1.265 -2.783 -1.335 -4.38 -.737s-2.643 2.06 -2.62 3.737v1c-3.245 .083 -6.135 -1.395 -8 -4c0 0 -4.182 7.433 4 11c-1.872 1.247 -3.739 2.088 -6 2c3.308 1.803 6.913 2.423 10.034 1.517c3.58 -1.04 6.522 -3.723 7.651 -7.742a13.84 13.84 0 0 0 .497 -3.753c-.002 -.249 1.51 -2.772 1.818 -4.013z" /></svg></a><a href="https://bsky.app/intent/compose?text=Structured%20Logging%20https%3A%2F%2Fkartar.net%2F2015%2F12%2Fstructured-logging%2F" target="_blank" rel="noopener noreferrer" class="p-2 rounded-sm bg-card border text-muted-foreground hover:text-primary hover:border-primary transition-colors" aria-label="Share on Bluesky" title="Share on Bluesky"><svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6.335 5.144c-1.654 -1.199 -4.335 -2.127 -4.335 .826c0 .59 .35 4.953 .556 5.661c.713 2.463 3.13 2.75 5.444 2.369c-4.045 .665 -4.889 3.208 -2.667 5.41c1.03 1.018 1.913 1.59 2.667 1.59c2 0 3.134 -2.769 3.5 -3.5c.333 -.667 .5 -1.167 .5 -1.5c0 .333 .167 .833 .5 1.5c.366 .731 1.5 3.5 3.5 3.5c.754 0 1.637 -.571 2.667 -1.59c2.222 -2.203 1.378 -4.746 -2.667 -5.41c2.314 .38 4.73 .094 5.444 -2.369c.206 -.708 .556 -5.072 .556 -5.661c0 -2.953 -2.68 -2.025 -4.335 -.826c-2.293 1.662 -4.76 5.048 -5.665 6.856c-.905 -1.808 -3.372 -5.194 -5.665 -6.856" /></svg></a><a href="https://tootpick.org/#text=Structured%20Logging%20https%3A%2F%2Fkartar.net%2F2015%2F12%2Fstructured-logging%2F" target="_blank" rel="noopener noreferrer" class="p-2 rounded-sm bg-card border text-muted-foreground hover:text-primary hover:border-primary transition-colors" aria-label="Share on Mastodon" title="Share on Mastodon"><svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M18.648 15.254c-1.816 1.763 -6.648 1.626 -6.648 1.626a18.262 18.262 0 0 1 -3.288 -.256c1.127 1.985 4.12 2.81 8.982 2.475c-1.945 2.013 -13.598 5.257 -13.668 -7.636l-.026 -1.154c0 -3.036 .023 -4.115 1.352 -5.633c1.671 -1.91 6.648 -1.666 6.648 -1.666s4.977 -.243 6.648 1.667c1.329 1.518 1.352 2.597 1.352 5.633s-.456 4.074 -1.352 4.944" /><path d="M12 11.204v-2.926c0 -1.258 -.895 -2.278 -2 -2.278s-2 1.02 -2 2.278v4.722m4 -4.722c0 -1.258 .895 -2.278 2 -2.278s2 1.02 2 2.278v4.722" /></svg></a><a href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fkartar.net%2F2015%2F12%2Fstructured-logging%2F" target="_blank" rel="noopener noreferrer" class="p-2 rounded-sm bg-card border text-muted-foreground hover:text-primary hover:border-primary transition-colors" aria-label="Share on Facebook" title="Share on Facebook"><svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 10v4h3v7h4v-7h3l1 -4h-4v-2a1 1 0 0 1 1 -1h3v-4h-3a5 5 0 0 0 -5 5v2h-3" /></svg></a><a href="https://www.linkedin.com/shareArticle?mini=true&amp;url=https%3A%2F%2Fkartar.net%2F2015%2F12%2Fstructured-logging%2F&amp;title=Structured%20Logging" target="_blank" rel="noopener noreferrer" class="p-2 rounded-sm bg-card border text-muted-foreground hover:text-primary hover:border-primary transition-colors" aria-label="Share on LinkedIn" title="Share on LinkedIn"><svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><rect x="4" y="4" width="16" height="16" rx="2" /><line x1="8" y1="11" x2="8" y2="16" /><line x1="8" y1="8" x2="8" y2="8.01" /><line x1="12" y1="16" x2="12" y2="11" /><path d="M16 16v-3a2 2 0 0 0 -4 0" /></svg></a><a href="mailto:?subject=Structured%20Logging&amp;body=https%3A%2F%2Fkartar.net%2F2015%2F12%2Fstructured-logging%2F" target="_blank" rel="noopener noreferrer" class="p-2 rounded-sm bg-card border text-muted-foreground hover:text-primary hover:border-primary transition-colors" aria-label="Share on Email" title="Share on Email"><svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><rect x="3" y="5" width="18" height="14" rx="2" /><polyline points="3 7 12 13 21 7" /></svg></a></div></div></div><nav class="col-start-2 grid grid-cols-1 gap-x-8 gap-y-4 font-mono text-sm sm:grid-cols-2" aria-label="Post navigation"><a href="/blog/junior-engineers#post-title" class="group text-muted-foreground hover:text-foreground block transition-colors"><div class="text-muted-foreground/70 text-xs sm:text-left"><span class="text-foreground/60">$</span> cd ../prev</div><div class="text-foreground truncate sm:text-left"><span class="text-muted-foreground/70">← </span>So what exactly is a junior software engineer?</div></a><a href="/blog/managing-maintenance-with-riemann#post-title" class="group text-muted-foreground hover:text-foreground block transition-colors"><div class="text-muted-foreground/70 text-xs sm:text-right"><span class="text-foreground/60">$</span> cd ../next</div><div class="text-foreground truncate sm:text-right">Managing maintenance with Riemann<span class="text-muted-foreground/70"> →</span></div></a></nav></section><button data-slot="button" class="items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 size-9 group fixed right-8 bottom-8 z-50 hidden" id="scroll-to-top" title="Scroll to top" aria-label="Scroll to top"><svg width="1em" height="1em" class="mx-auto size-4 transition-all group-hover:-translate-y-0.5" data-icon="lucide:arrow-up"><symbol id="ai:lucide:arrow-up" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m5 12l7-7l7 7m-7 7V5"/></symbol><use href="#ai:lucide:arrow-up"></use></svg></button><script type="module">document.addEventListener(`astro:page-load`,()=>{let e=document.getElementById(`scroll-to-top`),t=document.querySelector(`footer`);e&&t&&(e.addEventListener(`click`,()=>{let e=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches;window.scrollTo({top:0,behavior:e?`auto`:`smooth`})}),window.addEventListener(`scroll`,()=>{let n=t.getBoundingClientRect().top<=window.innerHeight;e.classList.toggle(`hidden`,window.scrollY<=300||n)}))});</script></main></div></div><script type="module">var e=()=>{document.querySelector(`.tmux-identity-aside`)?.classList.remove(`is-open`),document.querySelector(`.identity-backdrop`)?.classList.add(`hidden`),document.body.classList.remove(`overflow-hidden`)},t=()=>{document.querySelector(`.tmux-identity-aside`)?.classList.add(`is-open`),document.querySelector(`.identity-backdrop`)?.classList.remove(`hidden`),document.body.classList.add(`overflow-hidden`)};document.addEventListener(`click`,n=>{let r=n.target;r.closest(`.identity-toggle`)?(n.preventDefault(),document.querySelector(`.tmux-identity-aside`)?.classList.contains(`is-open`)?e():t()):r.closest(`.identity-backdrop`)&&e()}),document.addEventListener(`keydown`,t=>{t.key===`Escape`&&e()}),document.addEventListener(`astro:after-swap`,e);</script></body></html><script type="module">var e=()=>{if(!document.querySelector(`.katex`)||document.querySelector(`link[href*="katex.min.css"]`))return;let e=document.createElement(`link`);e.rel=`stylesheet`,e.href=`https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css`,document.head.appendChild(e)};document.addEventListener(`astro:page-load`,e),document.addEventListener(`astro:after-swap`,e);</script>