diff --git a/404.html b/404.html index fa31fc4..ed1a4de 100644 --- a/404.html +++ b/404.html @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + @@ -598,10 +598,10 @@

404 - Not found

- + - + diff --git a/assets/javascripts/bundle.a51614de.min.js b/assets/javascripts/bundle.220ee61c.min.js similarity index 71% rename from assets/javascripts/bundle.a51614de.min.js rename to assets/javascripts/bundle.220ee61c.min.js index 5afb782..116072a 100644 --- a/assets/javascripts/bundle.a51614de.min.js +++ b/assets/javascripts/bundle.220ee61c.min.js @@ -24,6 +24,6 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */var wr=function(e,t){return wr=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(r[o]=n[o])},wr(e,t)};function ie(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");wr(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}function fn(e,t,r,n){function o(i){return i instanceof r?i:new r(function(s){s(i)})}return new(r||(r=Promise))(function(i,s){function a(u){try{c(n.next(u))}catch(p){s(p)}}function f(u){try{c(n.throw(u))}catch(p){s(p)}}function c(u){u.done?i(u.value):o(u.value).then(a,f)}c((n=n.apply(e,t||[])).next())})}function $t(e,t){var r={label:0,sent:function(){if(i[0]&1)throw i[1];return i[1]},trys:[],ops:[]},n,o,i,s;return s={next:a(0),throw:a(1),return:a(2)},typeof Symbol=="function"&&(s[Symbol.iterator]=function(){return this}),s;function a(c){return function(u){return f([c,u])}}function f(c){if(n)throw new TypeError("Generator is already executing.");for(;r;)try{if(n=1,o&&(i=c[0]&2?o.return:c[0]?o.throw||((i=o.return)&&i.call(o),0):o.next)&&!(i=i.call(o,c[1])).done)return i;switch(o=0,i&&(c=[c[0]&2,i.value]),c[0]){case 0:case 1:i=c;break;case 4:return r.label++,{value:c[1],done:!1};case 5:r.label++,o=c[1],c=[0];continue;case 7:c=r.ops.pop(),r.trys.pop();continue;default:if(i=r.trys,!(i=i.length>0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof et?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function pn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,f){s=e[i](s),o(a,f,s.done,s.value)})}}function o(i,s,a,f){Promise.resolve(f).then(function(c){i({value:c,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: `+r.map(function(n,o){return o+1+") "+n.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),f=a.next();!f.done;f=a.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{ln(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ln(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function ln(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new xn(r,n)},t}(F);var xn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,f=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Sn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Oe=new Sn(wn);var M=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Te(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=zi();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return un(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return Ni(e);if(pt(e))return qi(e);if(Nt(e))return Ki(e);if(Kt(e))return On(e);if(Gt(e))return Qi(e);if(Jt(e))return Yi(e)}throw Qt(e)}function Ni(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function qi(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Dn(function(){return new Zt}))}}function Vn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,f=a===void 0?!0:a;return function(c){var u,p,m,d=0,h=!1,v=!1,Y=function(){p==null||p.unsubscribe(),p=void 0},B=function(){Y(),u=m=void 0,h=v=!1},N=function(){var O=u;B(),O==null||O.unsubscribe()};return y(function(O,Qe){d++,!v&&!h&&Y();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,f))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,Y(),p=$r(B,o,$e),De.error($e)},complete:function(){h=!0,Y(),p=$r(B,s),De.complete()}}),U(O).subscribe(u))})(c)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),J())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Kn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>rr(e)),V(rr(e)))}var Yn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Wr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),va?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Wr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ba.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Gn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Jn=typeof WeakMap!="undefined"?new WeakMap:new Yn,Xn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ga.getInstance(),n=new La(t,r,this);Jn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Xn.prototype[e]=function(){var t;return(t=Jn.get(this))[e].apply(t,arguments)}});var Aa=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Xn}(),Zn=Aa;var eo=new x,Ca=$(()=>k(new Zn(e=>{for(let t of e)eo.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ca.pipe(S(t=>t.observe(e)),g(t=>eo.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var to=new x,Ra=$(()=>k(new IntersectionObserver(e=>{for(let t of e)to.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function sr(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function ro(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),J())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function no(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function ka(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ha(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function oo(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:no("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!ka(n,r)}return!0}),pe());return Ha().pipe(g(t=>t?M:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function io(){return new x}function ao(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)ao(e,r)}function _(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)ao(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function so(){return location.hash.substring(1)}function Dr(e){let t=_("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Pa(e){return L(b(window,"hashchange"),e).pipe(l(so),V(so()),A(t=>t.length>0),X(1))}function co(e){return Pa(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function Vr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function fo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function zr(e,t){return e.pipe(g(r=>r?t():M))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>M),g(r=>r.status!==200?Ot(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),X(1))}function uo(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),X(1))}function pr(e){let t=_("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Ot(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function po(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function lo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(po),V(po()))}function mo(){return{width:innerWidth,height:innerHeight}}function ho(){return b(window,"resize",{passive:!0}).pipe(l(mo),V(mo()))}function bo(){return G([lo(),ho()]).pipe(l(([e,t])=>({offset:e,size:t})),X(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(ee("size")),o=G([n,r]).pipe(l(()=>Xe(e)));return G([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:f,y:c}])=>({offset:{x:s.x-f,y:s.y-c+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,f,c)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:f,error:c});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` + - + diff --git a/docs/CHANGELOG/index.html b/docs/CHANGELOG/index.html index 83d3969..07af5fd 100644 --- a/docs/CHANGELOG/index.html +++ b/docs/CHANGELOG/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -81,7 +81,7 @@
- + Skip to content @@ -318,11 +318,11 @@
- + - + diff --git a/docs/CODE_TAG_SUMMARY/index.html b/docs/CODE_TAG_SUMMARY/index.html index 393c98e..11fcf7e 100644 --- a/docs/CODE_TAG_SUMMARY/index.html +++ b/docs/CODE_TAG_SUMMARY/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -669,10 +669,10 @@

Collected Code Tags - + - + diff --git a/docs/DEVELOPER_GUIDE/index.html b/docs/DEVELOPER_GUIDE/index.html index 5f31197..da5f2ad 100644 --- a/docs/DEVELOPER_GUIDE/index.html +++ b/docs/DEVELOPER_GUIDE/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -813,10 +813,10 @@

Current Status - + - + diff --git a/docs/STYLE_GUIDE/index.html b/docs/STYLE_GUIDE/index.html index fc2a612..90b357b 100644 --- a/docs/STYLE_GUIDE/index.html +++ b/docs/STYLE_GUIDE/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -976,10 +976,10 @@

ADRs
- + - + diff --git a/index.html b/index.html index 744ec93..37cc972 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -877,10 +877,10 @@

License - + - + diff --git a/modules/tail_jsonl/_code_diagrams/index.html b/modules/tail_jsonl/_code_diagrams/index.html index a45a81d..826718a 100644 --- a/modules/tail_jsonl/_code_diagrams/index.html +++ b/modules/tail_jsonl/_code_diagrams/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -672,11 +672,11 @@

ClassesJune 17, 2023 + July 23, 2023
Created: - June 17, 2023 + July 23, 2023 @@ -718,10 +718,10 @@

Classes - + - + diff --git a/modules/tail_jsonl/classes.svg b/modules/tail_jsonl/classes.svg index 7d0f1b2..526f7e1 100644 --- a/modules/tail_jsonl/classes.svg +++ b/modules/tail_jsonl/classes.svg @@ -1,57 +1,57 @@ - - - + + classes - + tail_jsonl.config.Config - -Config - -keys -styles : Styles - - + +Config + +keys +styles : Styles + + tail_jsonl.config.Keys - -Keys - -level : List[str] -message : List[str] -on_own_line : List[str] -timestamp : List[str] - - + +Keys + +level : List[str] +message : List[str] +on_own_line : List[str] +timestamp : List[str] + + tail_jsonl.config.Keys->tail_jsonl.config.Config - - -keys + + +keys tail_jsonl._private.core.Record - -Record - -data : dict, Dict -level : str -message : str -timestamp : str - -from_line(data: Dict, config: Config): 'Record' + +Record + +data : dict, Dict +level : str +message : str +timestamp : str + +from_line(data: Dict, config: Config): 'Record' diff --git a/modules/tail_jsonl/packages.svg b/modules/tail_jsonl/packages.svg index 6ecbeb3..8284624 100644 --- a/modules/tail_jsonl/packages.svg +++ b/modules/tail_jsonl/packages.svg @@ -1,55 +1,55 @@ - - + packages - + tail_jsonl - -tail_jsonl + +tail_jsonl tail_jsonl._private - -tail_jsonl._private + +tail_jsonl._private tail_jsonl._private.core - -tail_jsonl._private.core + +tail_jsonl._private.core tail_jsonl.config - -tail_jsonl.config + +tail_jsonl.config tail_jsonl.scripts - -tail_jsonl.scripts + +tail_jsonl.scripts tail_jsonl.scripts->tail_jsonl._private.core - - + + tail_jsonl.scripts->tail_jsonl.config - - + + diff --git a/reference/SUMMARY/index.html b/reference/SUMMARY/index.html index 2f5778e..552748d 100644 --- a/reference/SUMMARY/index.html +++ b/reference/SUMMARY/index.html @@ -15,7 +15,7 @@ + @@ -23,7 +23,7 @@ - + @@ -587,11 +587,11 @@

SUMMARY

Last update: - June 17, 2023 + July 23, 2023
Created: - June 17, 2023 + July 23, 2023
@@ -633,10 +633,10 @@

SUMMARY

- + - + diff --git a/reference/tail_jsonl/_private/core/index.html b/reference/tail_jsonl/_private/core/index.html index f826ec2..b152da6 100644 --- a/reference/tail_jsonl/_private/core/index.html +++ b/reference/tail_jsonl/_private/core/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -812,23 +812,22 @@

Classes - Record + Record ⚓︎

-

- Bases: BaseModel

+

+ Bases: BaseModel

Record Model.

- -
- Source code in tail_jsonl/_private/core.py -
class Record(BaseModel):
+            
+ Source code in tail_jsonl/_private/core.py +
class Record(BaseModel):
     """Record Model."""
 
     timestamp: str
@@ -847,7 +846,7 @@ 

data=data, )

-
+
@@ -861,12 +860,13 @@

Functions⚓︎

+
- from_line + from_line @@ -881,9 +881,9 @@

Extract Record from jsonl.

-
- Source code in tail_jsonl/_private/core.py -
@classmethod
+          
+ Source code in tail_jsonl/_private/core.py +
@classmethod
 @beartype
 def from_line(cls, data: Dict, config: Config) -> 'Record':  # type: ignore[type-arg]
     """Extract Record from jsonl."""
@@ -894,7 +894,7 @@ 
data=data, )
-
+
@@ -908,12 +908,13 @@

Functions⚓︎

+

- pop_key + pop_key ⚓︎

@@ -924,24 +925,25 @@

Safely find the first key in the data or default to the fallback.

-
- Source code in tail_jsonl/_private/core.py -
@beartype
+          
+ Source code in tail_jsonl/_private/core.py +
@beartype
 def pop_key(data: Dict, keys: List[str], fallback: str) -> Any:  # type: ignore[type-arg]
     """Safely find the first key in the data or default to the fallback."""
     return _pop_key(data, copy(keys), fallback)
 
-
+

+

- print_record + print_record ⚓︎

@@ -952,9 +954,9 @@

Format and print the record.

-
- Source code in tail_jsonl/_private/core.py -
@beartype
+          
+ Source code in tail_jsonl/_private/core.py +
@beartype
 def print_record(line: str, console: Console, config: Config) -> None:
     """Format and print the record."""
     try:
@@ -983,7 +985,7 @@ 

**{f'_{key}' if key in keys else key: value for key, value in record.data.items()}, )

-
+

@@ -1001,11 +1003,11 @@

Last update: - June 17, 2023 + July 23, 2023
Created: - June 17, 2023 + July 23, 2023
@@ -1047,10 +1049,10 @@

- + - + diff --git a/reference/tail_jsonl/_private/index.html b/reference/tail_jsonl/_private/index.html index c52c99a..23f3e63 100644 --- a/reference/tail_jsonl/_private/index.html +++ b/reference/tail_jsonl/_private/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -636,11 +636,11 @@

_private

Last update: - June 17, 2023 + July 23, 2023
Created: - June 17, 2023 + July 23, 2023
@@ -682,10 +682,10 @@

_private

- + - + diff --git a/reference/tail_jsonl/config/index.html b/reference/tail_jsonl/config/index.html index 56e22be..5b46ef8 100644 --- a/reference/tail_jsonl/config/index.html +++ b/reference/tail_jsonl/config/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -718,29 +718,28 @@

Classes - Config + Config ⚓︎

-

- Bases: BaseModel

+

+ Bases: BaseModel

tail-jsonl config.

- -
- Source code in tail_jsonl/config.py -
class Config(BaseModel):
+            
+ Source code in tail_jsonl/config.py +
class Config(BaseModel):
     """`tail-jsonl` config."""
 
     styles: Styles = Field(default_factory=Styles)
     keys: Keys = Field(default_factory=Keys)
 
-
+
@@ -767,23 +766,22 @@

- Keys + Keys ⚓︎

-

- Bases: BaseModel

+

+ Bases: BaseModel

Special Keys.

- -
- Source code in tail_jsonl/config.py -
class Keys(BaseModel):
+            
+ Source code in tail_jsonl/config.py +
class Keys(BaseModel):
     """Special Keys."""
 
     timestamp: List[str] = Field(default_factory=lambda: ['timestamp', 'record.time.repr'])
@@ -792,7 +790,7 @@ 

on_own_line: List[str] = Field(default_factory=lambda: ['text', 'exception'])

-
+
@@ -828,11 +826,11 @@

Last update: - June 17, 2023 + July 23, 2023
Created: - June 17, 2023 + July 23, 2023
@@ -874,10 +872,10 @@

- + - + diff --git a/reference/tail_jsonl/index.html b/reference/tail_jsonl/index.html index 9b76bb8..22999d3 100644 --- a/reference/tail_jsonl/index.html +++ b/reference/tail_jsonl/index.html @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -636,11 +636,11 @@

tail_jsonl

Last update: - June 17, 2023 + July 23, 2023
Created: - June 17, 2023 + July 23, 2023
@@ -682,10 +682,10 @@

tail_jsonl

- + - + diff --git a/reference/tail_jsonl/scripts/index.html b/reference/tail_jsonl/scripts/index.html index 4af6e41..a78dba5 100644 --- a/reference/tail_jsonl/scripts/index.html +++ b/reference/tail_jsonl/scripts/index.html @@ -19,7 +19,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -712,12 +712,13 @@

scripts

Classes⚓︎

Functions⚓︎

+

- start + start ⚓︎

@@ -728,9 +729,9 @@

CLI Entrypoint.

-
- Source code in tail_jsonl/scripts.py -
@beartype
+          
+ Source code in tail_jsonl/scripts.py +
@beartype
 def start() -> None:  # pragma: no cover
     """CLI Entrypoint."""
     # PLANNED: Add a flag (--debug & store_true) to print debugging information
@@ -749,7 +750,7 @@ 

for line in fileinput.input(): print_record(line, console, config)

-
+

@@ -767,11 +768,11 @@

Last update: - June 17, 2023 + July 23, 2023
Created: - June 17, 2023 + July 23, 2023
@@ -813,10 +814,10 @@

- + - + diff --git a/search/search_index.json b/search/search_index.json index 28cb233..9bb8eff 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"tail-jsonl","text":"

Tail JSONL Logs

"},{"location":"#background","title":"Background","text":"

I wanted to find a tool that could:

  1. Convert a stream of JSONL logs into a readable logfmt-like output with minimal configuration
  2. Show exceptions on their own line

I investigated a lot of alternatives such as: humanlog, lnav, goaccess, angle-grinder, jq, textualog, etc. but nothing would both cleanly format the JSONL data and show the exception.

"},{"location":"#installation","title":"Installation","text":"

Install with pipx

pipx install tail-jsonl\n
"},{"location":"#usage","title":"Usage","text":"

Pipe JSONL output from any file, kubernetes (such as stern), Docker, etc.

# Example piping input in shell\necho '{\"message\": \"message\", \"timestamp\": \"2023-01-01T01:01:01.0123456Z\", \"level\": \"debug\", \"data\": true, \"more-data\": [null, true, -123.123]}' | tail-jsonl\ncat tests/data/logs.jsonl | tail-jsonl\n\n# Optionally, pre-filter or format with jq, grep, awk, or other tools\ncat tests/data/logs.jsonl | jq '.record' --compact-output | tail-jsonl\n\n# An example stern command (also consider -o=extjson)\nstern envvars --context staging --container gateway --since=\"60m\" --output raw | tail-jsonl\n\n# Or with Docker Compose (note that awk, cut, and grep all buffer. For awk, add '; system(\"\")')\ndocker compose logs --follow | awk 'match($0, / \\| \\{.+/) { print substr($0, RSTART+3, RLENGTH); system(\"\") }' | tail-jsonl\n
"},{"location":"#configuration","title":"Configuration","text":"

Optionally, specify a path to a custom configuration file. See an example configuration file at: tests/config_default.toml

echo '...' | tail-jsonl --config-path=~/.tail-jsonl.toml\n
"},{"location":"#project-status","title":"Project Status","text":"

See the Open Issues and/or the CODE_TAG_SUMMARY. For release history, see the CHANGELOG.

"},{"location":"#contributing","title":"Contributing","text":"

We welcome pull requests! For your pull request to be accepted smoothly, we suggest that you first open a GitHub issue to discuss your idea. For resources on getting started with the code base, see the below documentation:

  • DEVELOPER_GUIDE
  • STYLE_GUIDE
"},{"location":"#code-of-conduct","title":"Code of Conduct","text":"

We follow the Contributor Covenant Code of Conduct.

"},{"location":"#open-source-status","title":"Open Source Status","text":"

We try to reasonably meet most aspects of the \u201cOpenSSF scorecard\u201d from Open Source Insights

"},{"location":"#responsible-disclosure","title":"Responsible Disclosure","text":"

If you have any security issue to report, please contact the project maintainers privately. You can reach us at dev.act.kyle@gmail.com.

"},{"location":"#license","title":"License","text":"

LICENSE

"},{"location":"docs/Advanced_Documentation/","title":"Docs","text":"

Developer documentation

"},{"location":"docs/CHANGELOG/","title":"CHANGELOG","text":""},{"location":"docs/CHANGELOG/#124-2023-06-17","title":"1.2.4 (2023-06-17)","text":""},{"location":"docs/CHANGELOG/#fix","title":"Fix","text":"
  • don\u2019t highlight non-JSONL log lines
  • bump minimum pymdown dependency
"},{"location":"docs/CHANGELOG/#123-2023-03-11","title":"1.2.3 (2023-03-11)","text":""},{"location":"docs/CHANGELOG/#fix_1","title":"Fix","text":"
  • remove the trailing new line and formatting on raw text
"},{"location":"docs/CHANGELOG/#122-2023-02-25","title":"1.2.2 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#fix_2","title":"Fix","text":"
  • ensure keys are unique without losing any arguments
"},{"location":"docs/CHANGELOG/#121-2023-02-25","title":"1.2.1 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#fix_3","title":"Fix","text":"
  • resolve bugs in tail-jsonl config-path parsing
"},{"location":"docs/CHANGELOG/#120-2023-02-25","title":"1.2.0 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#feat","title":"Feat","text":"
  • update color scheme and resolve log issues
"},{"location":"docs/CHANGELOG/#refactor","title":"Refactor","text":"
  • migrate to corallium\u2019s rich_printer and styles

  • begin migration to corallium and calcipy v1 with copier template

"},{"location":"docs/CHANGELOG/#112-2023-02-16","title":"1.1.2 (2023-02-16)","text":""},{"location":"docs/CHANGELOG/#fix_4","title":"Fix","text":"
  • show non-JSON lines
"},{"location":"docs/CHANGELOG/#111-2023-02-06","title":"1.1.1 (2023-02-06)","text":""},{"location":"docs/CHANGELOG/#feat_1","title":"Feat","text":"
  • use a symbol to indicate each line
  • indent by four spaces on wrap
  • add version arg
  • indent folded text
"},{"location":"docs/CHANGELOG/#fix_5","title":"Fix","text":"
  • bump 1.1 without release
  • test loading user config and escaping dot-notation
"},{"location":"docs/CHANGELOG/#refactor_1","title":"Refactor","text":"
  • end didn\u2019t work as hoped
  • switch to beartype.typing
  • folded text doesn\u2019t work as expected
"},{"location":"docs/CHANGELOG/#100-2023-01-31","title":"1.0.0 (2023-01-31)","text":""},{"location":"docs/CHANGELOG/#feat_2","title":"Feat","text":"
  • add option for configuration file
"},{"location":"docs/CHANGELOG/#001-2023-01-30","title":"0.0.1 (2023-01-30)","text":""},{"location":"docs/CHANGELOG/#feat_3","title":"Feat","text":"
  • first pass at the package
  • initialize the repository
"},{"location":"docs/CODE_TAG_SUMMARY/","title":"Collected Code Tags","text":"Type Comment Last Edit Source File PLANNED Add a flag (\u2013debug & store_true) to print debugging information 2023-02-02 tail_jsonl/scripts.py:30

Found code tags for PLANNED (1)

"},{"location":"docs/DEVELOPER_GUIDE/","title":"Developer Notes","text":""},{"location":"docs/DEVELOPER_GUIDE/#local-development","title":"Local Development","text":"
git clone https://github.com/kyleking/tail-jsonl.git\ncd tail-jsonl\npoetry install --sync\n\n# See the available tasks\npoetry run calcipy\n# Or use a local 'run' file (so that 'calcipy' can be extended)\n./run\n\n# Run the default task list (lint, auto-format, test coverage, etc.)\n./run main\n\n# Make code changes and run specific tasks as needed:\n./run lint.fix test\n
"},{"location":"docs/DEVELOPER_GUIDE/#publishing","title":"Publishing","text":"

For testing, create an account on TestPyPi. Replace ... with the API token generated on TestPyPi or PyPi respectively

poetry config repositories.testpypi https://test.pypi.org/legacy/\npoetry config pypi-token.testpypi ...\n\n./run main pack.publish --to-test-pypi\n# If you didn't configure a token, you will need to provide your username and password to publish\n

To publish to the real PyPi

poetry config pypi-token.pypi ...\n./run release\n\n# Or for a pre-release\n./run release --suffix=rc\n
"},{"location":"docs/DEVELOPER_GUIDE/#current-status","title":"Current Status","text":"File Statements Missing Excluded Coverage tail_jsonl/__init__.py 3 0 0 100.0% tail_jsonl/_private/__init__.py 0 0 0 100.0% tail_jsonl/_private/core.py 49 1 0 97.1% tail_jsonl/config.py 11 0 0 100.0% tail_jsonl/scripts.py 18 0 11 95.5% Totals 81 1 11 97.3%

Generated on: 2023-06-17

"},{"location":"docs/STYLE_GUIDE/","title":"Personal Style Guides","text":""},{"location":"docs/STYLE_GUIDE/#git","title":"Git","text":"

We use Commitizen to manage both an auto-generated Changelog and incrementing the release version following semver. For both of these automated outputs to work well, please follow the Conventional Commits style, which is described in more detail below.

"},{"location":"docs/STYLE_GUIDE/#commitizen-types-and-scopes","title":"Commitizen Types and Scopes","text":"

type(scope): description

  • Types
  • fix: A bug fix
  • feat: A new feature
  • docs: Documentation-only changes (code comments, separate docs)
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons)
  • perf: A code change that improves performance
  • refactor: A change to production code that is not a fix, feat, or perf
  • test: Adding missing or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to our CI configuration files and scripts
  • A ! can be used to indicate a breaking change (refactor!: drop support for Node 6)
  • SemVer Rules
    • Based on commit type, the version will be auto-incremented: fix : PATCH // feat : MINOR // BREAKING CHANGE : MAJOR
  • Scopes
  • A Class, File name, Issue Number, other appropriate noun. As examples: build(poetry): bump requests to v3 or style(#32): add missing type annotations
  • Tips
  • What if a commit fits multiple types?
    • Go back and make multiple commits whenever possible. Part of the benefit of Conventional Commits is the focus on more organized and intentional changes
  • Use git rebase -i to fix commit names prior to merging if incorrect types/scopes are used
"},{"location":"docs/STYLE_GUIDE/#git-description-guidelines","title":"Git Description Guidelines","text":"
  • Commit message guidelines
  • Full sentence with verb (lowercase) and concise description. Below are modified examples for Conventional Commits
    • fix(roles): bug in admin role permissions
    • feat(ui): implement new button design
    • build(pip): upgrade package to remove vulnerabilities
    • refactor: file structure to improve code readability
    • perf(cli): rewrite methods
    • feat(api): endpoints to implement new customer dashboard
  • How to write a good commit message
  • A diff will tell you what changed, but only the commit message can properly tell you why.
  • Keep in mind: This has all been said before.
  • From the seven rules of a great Git commit message:
      1. Try for 50 characters, but consider 72 the hard limit
      1. Use the body to explain what and why vs. how
"},{"location":"docs/STYLE_GUIDE/#issue-labels-and-milestones","title":"Issue Labels and Milestones","text":"

Personal Guide

  • For Issue Labels, see labels.yml
  • Milestones
  • Current Tasks: main milestone (name could change based on a specific project, sprint, or month)
  • Next Tasks
  • Blue Sky
Research
  • [Sane Github Labels](https://medium.com/@dave_lunny/sane-github-labels-c5d2e6004b63) and see [sensible-github-labels](https://github.com/Relequestual/sensible-github-labels) for full descriptions of each
    • \u201cit is much more helpful to see the status and type of all issues at a glance.\u201d
    • One of each:
      • Status: \u2026
        • Abandoned, Accepted, Available, Blocked, Completed, In Progress, On Hold, Pending, Review Needed, Revision Needed
      • Type: \u2026
        • Bug, Maintenance, Question, Enhancement
      • Priority: \u2026
        • Critical, High, Medium, Low
  • [Britecharts](https://britecharts.github.io/britecharts/github-labels.html)
    • Status: \u2026
      • On Review \u2013 Request that we are pondering if including or not
      • Needs Reproducing \u2013 For bugs that need to be reproduced in order to get fixed
      • Needs Design \u2013 Feature that needs a design
      • Ready to Go \u2013 Issue that has been defined and is ready to get started with
      • In Progress \u2013 Issue that is being worked on right now.
      • Completed \u2013 Finished feature or fix
    • Type: \u2026
      • Bug \u2013 An unexpected problem or unintended behavior
      • Feature \u2013 A new feature request
      • Maintenance \u2013 A regular maintenance chore or task, including refactors, build system, CI, performance improvements
      • Documentation \u2013 A documentation improvement task
      • Question \u2013 An issue or PR that needs more information or a user question
    • Not Included
      • Priority: They would add complexity and overhead due to the discussions, but could help with the roadmap
      • Technology Labels: It can create too much overhead, as properly tag with technologies all the issues could be time consuming
  • [Ian Bicking Blog](https://www.ianbicking.org/blog/2014/03/use-github-issues-to-organize-a-project.html)
    • Milestone Overview
      • What are we doing right now?
      • What aren\u2019t we doing right now?
        • 2a. Stuff we\u2019ll probably do soon
        • 2b. Stuff we probably won\u2019t do soon
      • What aren\u2019t we sure about?
    • Milestone Descriptions
      • Stuff we are doing right now: this is the \u201cmain\u201d milestone. We give it a name (like Alpha 2 or Strawberry Rhubarb Pie) and we write down what we are trying to accomplish with the milestone. We create a new milestone when we are ready for the next iteration.
      • Stuff we\u2019ll probably do soon: this is a standing \u201c**Next Tasks**\u201d milestone. We never change or rename this milestone.
        • We use a permanent \u201cNext Tasks\u201d milestone (as opposed to renaming it to \u201cAlpha 3\u201d or actual-next-iteration milestone) because we don\u2019t want to presume or default to including something in the real next iteration. When we\u2019re ready to start planning the next iteration we\u2019ll create a new milestone, and only deliberately move things into that milestone.
      • Stuff we probably won\u2019t do soon: this is a standing \u201c**Blue Sky**\u201d milestone. We refer to these tickets and sometimes look through them, but they are easy to ignore, somewhat intentionally ignored.
      • What aren\u2019t we sure about?: issues with no milestone.
    • Label: \u201cNeeds Discussion\u201d - (addressed in a triage meeting) - use liberally for either big or small tickets
    • \u201cIt\u2019s better to give people more power: it\u2019s actually helpful if people can overreach because it is an opportunity to establish where the limits really are and what purpose those limits have\u201d
    "},{"location":"docs/STYLE_GUIDE/#external-links","title":"External Links","text":"
    • Git: The Simple Guide
    • Commit Messages and why use the present tense
    • Github\u2019s Advice on Github
    • Most Comprehensive Guide
    • Git Pro Book (free)
    • Bash Tab-Completion Snippet
    "},{"location":"docs/STYLE_GUIDE/#python","title":"Python","text":"
    • Python Style Guides
    • https://gist.github.com/sloria/7001839
    • http://www.nilunder.com/blog/2013/08/03/pythonic-sensibilities/
    • https://innoq.github.io/cards42org_en/
    • https://docs.openstack.org/hacking/latest/user/hacking.html#styleguide
    • https://www.python.org/doc/humor/
    • https://docs.python-guide.org/writing/reading/
    • https://realpython.com/python-refactoring/
    "},{"location":"docs/STYLE_GUIDE/#adrs","title":"ADRs","text":"
    • ADR Approaches
      • https://infraeng.dev/tech-spec Template (And associated review) vs. https://infraeng.dev/decision-log/
      • Y-Statements: abbreviated shorthand. Add this as a one-line decision option if a full ADR isn\u2019t needed (or when referencing an existing ADR) (https://scribe.rip/@docsoc/y-statements-10eb07b5a177)
      • https://adr.github.io
      • More formal implementation of ADRs (MADR) that this is based on. Template: https://github.com/adr/madr/blob/97fb8edec60b8dc70b8166ef62de34c4e26b46c0/template/adr-template.md
      • https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-5639.md
    • Examples
      • https://github.com/pawamoy/mkdocstrings/issues/28
      • https://github.com/arachne-framework/architecture/blob/060a956277a5ad71df93da49fee52463408841af/adr-002-configuration.md
      • https://github.com/arachne-framework/architecture/tree/060a956277a5ad71df93da49fee52463408841af
      • https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-1010.md
      • https://docs-v1.prefect.io/core/pins/pin-01-introduce-pins.html
      • https://peps.python.org/pep-0387/
      • And many others!

    <\u2013 Links \u2013>

    "},{"location":"modules/tail_jsonl/_code_diagrams/","title":"Code Diagrams","text":"

    Auto-generated with pylint-pyreverse

    "},{"location":"modules/tail_jsonl/_code_diagrams/#packages","title":"Packages","text":"

    Full Size

    "},{"location":"modules/tail_jsonl/_code_diagrams/#classes","title":"Classes","text":"

    Full Size

    "},{"location":"reference/SUMMARY/","title":"SUMMARY","text":"
    • tail_jsonl
      • _private
        • core
      • config
      • scripts
    "},{"location":"reference/tail_jsonl/","title":"tail_jsonl","text":"

    tail_jsonl.

    "},{"location":"reference/tail_jsonl/config/","title":"config","text":"

    Configuration.

    "},{"location":"reference/tail_jsonl/config/#tail_jsonl.config-classes","title":"Classes","text":""},{"location":"reference/tail_jsonl/config/#tail_jsonl.config.Config","title":"Config","text":"

    Bases: BaseModel

    tail-jsonl config.

    Source code in tail_jsonl/config.py
    class Config(BaseModel):\n\"\"\"`tail-jsonl` config.\"\"\"\nstyles: Styles = Field(default_factory=Styles)\nkeys: Keys = Field(default_factory=Keys)\n
    "},{"location":"reference/tail_jsonl/config/#tail_jsonl.config.Keys","title":"Keys","text":"

    Bases: BaseModel

    Special Keys.

    Source code in tail_jsonl/config.py
    class Keys(BaseModel):\n\"\"\"Special Keys.\"\"\"\ntimestamp: List[str] = Field(default_factory=lambda: ['timestamp', 'record.time.repr'])\nlevel: List[str] = Field(default_factory=lambda: ['level', 'record.level.name'])\nmessage: List[str] = Field(default_factory=lambda: ['event', 'message', 'record.message'])\non_own_line: List[str] = Field(default_factory=lambda: ['text', 'exception'])\n
    "},{"location":"reference/tail_jsonl/scripts/","title":"scripts","text":"

    Start the command line program.

    "},{"location":"reference/tail_jsonl/scripts/#tail_jsonl.scripts-classes","title":"Classes","text":""},{"location":"reference/tail_jsonl/scripts/#tail_jsonl.scripts-functions","title":"Functions","text":""},{"location":"reference/tail_jsonl/scripts/#tail_jsonl.scripts.start","title":"start","text":"
    start()\n

    CLI Entrypoint.

    Source code in tail_jsonl/scripts.py
    @beartype\ndef start() -> None:  # pragma: no cover\n\"\"\"CLI Entrypoint.\"\"\"\n# PLANNED: Add a flag (--debug & store_true) to print debugging information\nparser = argparse.ArgumentParser(description='Pipe JSONL Logs for pretty printing')\nparser.add_argument(\n'-v', '--version', action='version',\nversion=f'%(prog)s {__version__}', help=\"Show program's version number and exit.\",\n)\nparser.add_argument('--config-path', help='Path to a configuration file')\noptions = parser.parse_args(sys.argv[1:])\nsys.argv = sys.argv[:1]  # Remove CLI before calling fileinput\nconfig = _load_config(options.config_path)\nconsole = Console()\nfor line in fileinput.input():\nprint_record(line, console, config)\n
    "},{"location":"reference/tail_jsonl/_private/","title":"_private","text":""},{"location":"reference/tail_jsonl/_private/core/","title":"core","text":"

    Core print logic.

    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core-classes","title":"Classes","text":""},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.Record","title":"Record","text":"

    Bases: BaseModel

    Record Model.

    Source code in tail_jsonl/_private/core.py
    class Record(BaseModel):\n\"\"\"Record Model.\"\"\"\ntimestamp: str\nlevel: str\nmessage: str\ndata: Dict  # type: ignore[type-arg]\n@classmethod\n@beartype\ndef from_line(cls, data: Dict, config: Config) -> 'Record':  # type: ignore[type-arg]\n\"\"\"Extract Record from jsonl.\"\"\"\nreturn cls(\ntimestamp=pop_key(data, config.keys.timestamp, '<no timestamp>'),\nlevel=pop_key(data, config.keys.level, ''),\nmessage=pop_key(data, config.keys.message, '<no message>'),\ndata=data,\n)\n
    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.Record-functions","title":"Functions","text":""},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.Record.from_line","title":"from_line classmethod","text":"
    from_line(data, config)\n

    Extract Record from jsonl.

    Source code in tail_jsonl/_private/core.py
    @classmethod\n@beartype\ndef from_line(cls, data: Dict, config: Config) -> 'Record':  # type: ignore[type-arg]\n\"\"\"Extract Record from jsonl.\"\"\"\nreturn cls(\ntimestamp=pop_key(data, config.keys.timestamp, '<no timestamp>'),\nlevel=pop_key(data, config.keys.level, ''),\nmessage=pop_key(data, config.keys.message, '<no message>'),\ndata=data,\n)\n
    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core-functions","title":"Functions","text":""},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.pop_key","title":"pop_key","text":"
    pop_key(data, keys, fallback)\n

    Safely find the first key in the data or default to the fallback.

    Source code in tail_jsonl/_private/core.py
    @beartype\ndef pop_key(data: Dict, keys: List[str], fallback: str) -> Any:  # type: ignore[type-arg]\n\"\"\"Safely find the first key in the data or default to the fallback.\"\"\"\nreturn _pop_key(data, copy(keys), fallback)\n
    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.print_record","title":"print_record","text":"
    print_record(line, console, config)\n

    Format and print the record.

    Source code in tail_jsonl/_private/core.py
    @beartype\ndef print_record(line: str, console: Console, config: Config) -> None:\n\"\"\"Format and print the record.\"\"\"\ntry:\nrecord = Record.from_line(json.loads(line), config=config)\nexcept Exception:  # noqa: PIE786\nconsole.print(line.rstrip(), markup=False, highlight=False)  # Print the unmodified line\nreturn\nif (_this_level := get_level(name=record.level)) == logging.NOTSET and record.level:\nrecord.data['_level_name'] = record.level\nprinter_kwargs = {\n'message': record.message,\n'is_header': False,\n'_this_level': _this_level,\n'_is_text': False,\n'_console': console,\n'_styles': config.styles,\n'_keys_on_own_line': config.keys.on_own_line,\n'timestamp': record.timestamp,\n}\nkeys = set(printer_kwargs)\nrich_printer(\n**printer_kwargs,\n# Ensure that there is no repeat keyword arguments\n**{f'_{key}' if key in keys else key: value for key, value in record.data.items()},\n)\n
    "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"tail-jsonl","text":"

    Tail JSONL Logs

    "},{"location":"#background","title":"Background","text":"

    I wanted to find a tool that could:

    1. Convert a stream of JSONL logs into a readable logfmt-like output with minimal configuration
    2. Show exceptions on their own line

    I investigated a lot of alternatives such as: humanlog, lnav, goaccess, angle-grinder, jq, textualog, etc. but nothing would both cleanly format the JSONL data and show the exception.

    "},{"location":"#installation","title":"Installation","text":"

    Install with pipx

    pipx install tail-jsonl\n
    "},{"location":"#usage","title":"Usage","text":"

    Pipe JSONL output from any file, kubernetes (such as stern), Docker, etc.

    # Example piping input in shell\necho '{\"message\": \"message\", \"timestamp\": \"2023-01-01T01:01:01.0123456Z\", \"level\": \"debug\", \"data\": true, \"more-data\": [null, true, -123.123]}' | tail-jsonl\ncat tests/data/logs.jsonl | tail-jsonl\n\n# Optionally, pre-filter or format with jq, grep, awk, or other tools\ncat tests/data/logs.jsonl | jq '.record' --compact-output | tail-jsonl\n\n# An example stern command (also consider -o=extjson)\nstern envvars --context staging --container gateway --since=\"60m\" --output raw | tail-jsonl\n\n# Or with Docker Compose (note that awk, cut, and grep all buffer. For awk, add '; system(\"\")')\ndocker compose logs --follow | awk 'match($0, / \\| \\{.+/) { print substr($0, RSTART+3, RLENGTH); system(\"\") }' | tail-jsonl\n
    "},{"location":"#configuration","title":"Configuration","text":"

    Optionally, specify a path to a custom configuration file. See an example configuration file at: tests/config_default.toml

    echo '...' | tail-jsonl --config-path=~/.tail-jsonl.toml\n
    "},{"location":"#project-status","title":"Project Status","text":"

    See the Open Issues and/or the CODE_TAG_SUMMARY. For release history, see the CHANGELOG.

    "},{"location":"#contributing","title":"Contributing","text":"

    We welcome pull requests! For your pull request to be accepted smoothly, we suggest that you first open a GitHub issue to discuss your idea. For resources on getting started with the code base, see the below documentation:

    • DEVELOPER_GUIDE
    • STYLE_GUIDE
    "},{"location":"#code-of-conduct","title":"Code of Conduct","text":"

    We follow the Contributor Covenant Code of Conduct.

    "},{"location":"#open-source-status","title":"Open Source Status","text":"

    We try to reasonably meet most aspects of the \u201cOpenSSF scorecard\u201d from Open Source Insights

    "},{"location":"#responsible-disclosure","title":"Responsible Disclosure","text":"

    If you have any security issue to report, please contact the project maintainers privately. You can reach us at dev.act.kyle@gmail.com.

    "},{"location":"#license","title":"License","text":"

    LICENSE

    "},{"location":"docs/Advanced_Documentation/","title":"Docs","text":"

    Developer documentation

    "},{"location":"docs/CHANGELOG/","title":"CHANGELOG","text":""},{"location":"docs/CHANGELOG/#unreleased","title":"Unreleased","text":""},{"location":"docs/CHANGELOG/#fix","title":"Fix","text":"
    • don\u2019t highlight non-JSONL log lines
    • bump minimum pymdown dependency
    "},{"location":"docs/CHANGELOG/#123-2023-03-11","title":"1.2.3 (2023-03-11)","text":""},{"location":"docs/CHANGELOG/#fix_1","title":"Fix","text":"
    • remove the trailing new line and formatting on raw text
    "},{"location":"docs/CHANGELOG/#122-2023-02-25","title":"1.2.2 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#fix_2","title":"Fix","text":"
    • ensure keys are unique without losing any arguments
    "},{"location":"docs/CHANGELOG/#121-2023-02-25","title":"1.2.1 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#fix_3","title":"Fix","text":"
    • resolve bugs in tail-jsonl config-path parsing
    "},{"location":"docs/CHANGELOG/#120-2023-02-25","title":"1.2.0 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#feat","title":"Feat","text":"
    • update color scheme and resolve log issues
    "},{"location":"docs/CHANGELOG/#refactor","title":"Refactor","text":"
    • migrate to corallium\u2019s rich_printer and styles

    • begin migration to corallium and calcipy v1 with copier template

    "},{"location":"docs/CHANGELOG/#112-2023-02-16","title":"1.1.2 (2023-02-16)","text":""},{"location":"docs/CHANGELOG/#fix_4","title":"Fix","text":"
    • show non-JSON lines
    "},{"location":"docs/CHANGELOG/#111-2023-02-06","title":"1.1.1 (2023-02-06)","text":""},{"location":"docs/CHANGELOG/#feat_1","title":"Feat","text":"
    • use a symbol to indicate each line
    • indent by four spaces on wrap
    • add version arg
    • indent folded text
    "},{"location":"docs/CHANGELOG/#fix_5","title":"Fix","text":"
    • bump 1.1 without release
    • test loading user config and escaping dot-notation
    "},{"location":"docs/CHANGELOG/#refactor_1","title":"Refactor","text":"
    • end didn\u2019t work as hoped
    • switch to beartype.typing
    • folded text doesn\u2019t work as expected
    "},{"location":"docs/CHANGELOG/#100-2023-01-31","title":"1.0.0 (2023-01-31)","text":""},{"location":"docs/CHANGELOG/#feat_2","title":"Feat","text":"
    • add option for configuration file
    "},{"location":"docs/CHANGELOG/#001-2023-01-30","title":"0.0.1 (2023-01-30)","text":""},{"location":"docs/CHANGELOG/#feat_3","title":"Feat","text":"
    • first pass at the package
    • initialize the repository
    "},{"location":"docs/CODE_TAG_SUMMARY/","title":"Collected Code Tags","text":"Type Comment Last Edit Source File PLANNED Add a flag (\u2013debug & store_true) to print debugging information 2023-02-02 tail_jsonl/scripts.py:30

    Found code tags for PLANNED (1)

    "},{"location":"docs/DEVELOPER_GUIDE/","title":"Developer Notes","text":""},{"location":"docs/DEVELOPER_GUIDE/#local-development","title":"Local Development","text":"
    git clone https://github.com/kyleking/tail-jsonl.git\ncd tail-jsonl\npoetry install --sync\n\n# See the available tasks\npoetry run calcipy\n# Or use a local 'run' file (so that 'calcipy' can be extended)\n./run\n\n# Run the default task list (lint, auto-format, test coverage, etc.)\n./run main\n\n# Make code changes and run specific tasks as needed:\n./run lint.fix test\n
    "},{"location":"docs/DEVELOPER_GUIDE/#publishing","title":"Publishing","text":"

    For testing, create an account on TestPyPi. Replace ... with the API token generated on TestPyPi or PyPi respectively

    poetry config repositories.testpypi https://test.pypi.org/legacy/\npoetry config pypi-token.testpypi ...\n\n./run main pack.publish --to-test-pypi\n# If you didn't configure a token, you will need to provide your username and password to publish\n

    To publish to the real PyPi

    poetry config pypi-token.pypi ...\n./run release\n\n# Or for a pre-release\n./run release --suffix=rc\n
    "},{"location":"docs/DEVELOPER_GUIDE/#current-status","title":"Current Status","text":"File Statements Missing Excluded Coverage tail_jsonl/__init__.py 3 0 0 100.0% tail_jsonl/_private/__init__.py 0 0 0 100.0% tail_jsonl/_private/core.py 49 1 0 97.1% tail_jsonl/config.py 11 0 0 100.0% tail_jsonl/scripts.py 18 0 11 95.5% Totals 81 1 11 97.3%

    Generated on: 2023-06-17

    "},{"location":"docs/STYLE_GUIDE/","title":"Personal Style Guides","text":""},{"location":"docs/STYLE_GUIDE/#git","title":"Git","text":"

    We use Commitizen to manage both an auto-generated Changelog and incrementing the release version following semver. For both of these automated outputs to work well, please follow the Conventional Commits style, which is described in more detail below.

    "},{"location":"docs/STYLE_GUIDE/#commitizen-types-and-scopes","title":"Commitizen Types and Scopes","text":"

    type(scope): description

    • Types
    • fix: A bug fix
    • feat: A new feature
    • docs: Documentation-only changes (code comments, separate docs)
    • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons)
    • perf: A code change that improves performance
    • refactor: A change to production code that is not a fix, feat, or perf
    • test: Adding missing or correcting existing tests
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to our CI configuration files and scripts
    • A ! can be used to indicate a breaking change (refactor!: drop support for Node 6)
    • SemVer Rules
      • Based on commit type, the version will be auto-incremented: fix : PATCH // feat : MINOR // BREAKING CHANGE : MAJOR
    • Scopes
    • A Class, File name, Issue Number, other appropriate noun. As examples: build(poetry): bump requests to v3 or style(#32): add missing type annotations
    • Tips
    • What if a commit fits multiple types?
      • Go back and make multiple commits whenever possible. Part of the benefit of Conventional Commits is the focus on more organized and intentional changes
    • Use git rebase -i to fix commit names prior to merging if incorrect types/scopes are used
    "},{"location":"docs/STYLE_GUIDE/#git-description-guidelines","title":"Git Description Guidelines","text":"
    • Commit message guidelines
    • Full sentence with verb (lowercase) and concise description. Below are modified examples for Conventional Commits
      • fix(roles): bug in admin role permissions
      • feat(ui): implement new button design
      • build(pip): upgrade package to remove vulnerabilities
      • refactor: file structure to improve code readability
      • perf(cli): rewrite methods
      • feat(api): endpoints to implement new customer dashboard
    • How to write a good commit message
    • A diff will tell you what changed, but only the commit message can properly tell you why.
    • Keep in mind: This has all been said before.
    • From the seven rules of a great Git commit message:
        1. Try for 50 characters, but consider 72 the hard limit
        1. Use the body to explain what and why vs. how
    "},{"location":"docs/STYLE_GUIDE/#issue-labels-and-milestones","title":"Issue Labels and Milestones","text":"

    Personal Guide

    • For Issue Labels, see labels.yml
    • Milestones
    • Current Tasks: main milestone (name could change based on a specific project, sprint, or month)
    • Next Tasks
    • Blue Sky
    Research
    • [Sane Github Labels](https://medium.com/@dave_lunny/sane-github-labels-c5d2e6004b63) and see [sensible-github-labels](https://github.com/Relequestual/sensible-github-labels) for full descriptions of each
      • \u201cit is much more helpful to see the status and type of all issues at a glance.\u201d
      • One of each:
        • Status: \u2026
          • Abandoned, Accepted, Available, Blocked, Completed, In Progress, On Hold, Pending, Review Needed, Revision Needed
        • Type: \u2026
          • Bug, Maintenance, Question, Enhancement
        • Priority: \u2026
          • Critical, High, Medium, Low
    • [Britecharts](https://britecharts.github.io/britecharts/github-labels.html)
      • Status: \u2026
        • On Review \u2013 Request that we are pondering if including or not
        • Needs Reproducing \u2013 For bugs that need to be reproduced in order to get fixed
        • Needs Design \u2013 Feature that needs a design
        • Ready to Go \u2013 Issue that has been defined and is ready to get started with
        • In Progress \u2013 Issue that is being worked on right now.
        • Completed \u2013 Finished feature or fix
      • Type: \u2026
        • Bug \u2013 An unexpected problem or unintended behavior
        • Feature \u2013 A new feature request
        • Maintenance \u2013 A regular maintenance chore or task, including refactors, build system, CI, performance improvements
        • Documentation \u2013 A documentation improvement task
        • Question \u2013 An issue or PR that needs more information or a user question
      • Not Included
        • Priority: They would add complexity and overhead due to the discussions, but could help with the roadmap
        • Technology Labels: It can create too much overhead, as properly tag with technologies all the issues could be time consuming
  • [Ian Bicking Blog](https://www.ianbicking.org/blog/2014/03/use-github-issues-to-organize-a-project.html)
    • Milestone Overview
      • What are we doing right now?
      • What aren\u2019t we doing right now?
        • 2a. Stuff we\u2019ll probably do soon
        • 2b. Stuff we probably won\u2019t do soon
      • What aren\u2019t we sure about?
    • Milestone Descriptions
      • Stuff we are doing right now: this is the \u201cmain\u201d milestone. We give it a name (like Alpha 2 or Strawberry Rhubarb Pie) and we write down what we are trying to accomplish with the milestone. We create a new milestone when we are ready for the next iteration.
      • Stuff we\u2019ll probably do soon: this is a standing \u201c**Next Tasks**\u201d milestone. We never change or rename this milestone.
        • We use a permanent \u201cNext Tasks\u201d milestone (as opposed to renaming it to \u201cAlpha 3\u201d or actual-next-iteration milestone) because we don\u2019t want to presume or default to including something in the real next iteration. When we\u2019re ready to start planning the next iteration we\u2019ll create a new milestone, and only deliberately move things into that milestone.
      • Stuff we probably won\u2019t do soon: this is a standing \u201c**Blue Sky**\u201d milestone. We refer to these tickets and sometimes look through them, but they are easy to ignore, somewhat intentionally ignored.
      • What aren\u2019t we sure about?: issues with no milestone.
    • Label: \u201cNeeds Discussion\u201d - (addressed in a triage meeting) - use liberally for either big or small tickets
    • \u201cIt\u2019s better to give people more power: it\u2019s actually helpful if people can overreach because it is an opportunity to establish where the limits really are and what purpose those limits have\u201d
    "},{"location":"docs/STYLE_GUIDE/#external-links","title":"External Links","text":"
    • Git: The Simple Guide
    • Commit Messages and why use the present tense
    • Github\u2019s Advice on Github
    • Most Comprehensive Guide
    • Git Pro Book (free)
    • Bash Tab-Completion Snippet
    "},{"location":"docs/STYLE_GUIDE/#python","title":"Python","text":"
    • Python Style Guides
    • https://gist.github.com/sloria/7001839
    • http://www.nilunder.com/blog/2013/08/03/pythonic-sensibilities/
    • https://innoq.github.io/cards42org_en/
    • https://docs.openstack.org/hacking/latest/user/hacking.html#styleguide
    • https://www.python.org/doc/humor/
    • https://docs.python-guide.org/writing/reading/
    • https://realpython.com/python-refactoring/
    "},{"location":"docs/STYLE_GUIDE/#adrs","title":"ADRs","text":"
    • ADR Approaches
      • https://infraeng.dev/tech-spec Template (And associated review) vs. https://infraeng.dev/decision-log/
      • Y-Statements: abbreviated shorthand. Add this as a one-line decision option if a full ADR isn\u2019t needed (or when referencing an existing ADR) (https://scribe.rip/@docsoc/y-statements-10eb07b5a177)
      • https://adr.github.io
      • More formal implementation of ADRs (MADR) that this is based on. Template: https://github.com/adr/madr/blob/97fb8edec60b8dc70b8166ef62de34c4e26b46c0/template/adr-template.md
      • https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-5639.md
    • Examples
      • https://github.com/pawamoy/mkdocstrings/issues/28
      • https://github.com/arachne-framework/architecture/blob/060a956277a5ad71df93da49fee52463408841af/adr-002-configuration.md
      • https://github.com/arachne-framework/architecture/tree/060a956277a5ad71df93da49fee52463408841af
      • https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-1010.md
      • https://docs-v1.prefect.io/core/pins/pin-01-introduce-pins.html
      • https://peps.python.org/pep-0387/
      • And many others!

    <\u2013 Links \u2013>

    "},{"location":"modules/tail_jsonl/_code_diagrams/","title":"Code Diagrams","text":"

    Auto-generated with pylint-pyreverse

    "},{"location":"modules/tail_jsonl/_code_diagrams/#packages","title":"Packages","text":"

    Full Size

    "},{"location":"modules/tail_jsonl/_code_diagrams/#classes","title":"Classes","text":"

    Full Size

    "},{"location":"reference/SUMMARY/","title":"SUMMARY","text":"
    • tail_jsonl
      • _private
        • core
      • config
      • scripts
    "},{"location":"reference/tail_jsonl/","title":"tail_jsonl","text":"

    tail_jsonl.

    "},{"location":"reference/tail_jsonl/config/","title":"config","text":"

    Configuration.

    "},{"location":"reference/tail_jsonl/config/#tail_jsonl.config-classes","title":"Classes","text":""},{"location":"reference/tail_jsonl/config/#tail_jsonl.config.Config","title":"Config","text":"

    Bases: BaseModel

    tail-jsonl config.

    Source code in tail_jsonl/config.py
    class Config(BaseModel):\n\"\"\"`tail-jsonl` config.\"\"\"\nstyles: Styles = Field(default_factory=Styles)\nkeys: Keys = Field(default_factory=Keys)\n
    "},{"location":"reference/tail_jsonl/config/#tail_jsonl.config.Keys","title":"Keys","text":"

    Bases: BaseModel

    Special Keys.

    Source code in tail_jsonl/config.py
    class Keys(BaseModel):\n\"\"\"Special Keys.\"\"\"\ntimestamp: List[str] = Field(default_factory=lambda: ['timestamp', 'record.time.repr'])\nlevel: List[str] = Field(default_factory=lambda: ['level', 'record.level.name'])\nmessage: List[str] = Field(default_factory=lambda: ['event', 'message', 'record.message'])\non_own_line: List[str] = Field(default_factory=lambda: ['text', 'exception'])\n
    "},{"location":"reference/tail_jsonl/scripts/","title":"scripts","text":"

    Start the command line program.

    "},{"location":"reference/tail_jsonl/scripts/#tail_jsonl.scripts-classes","title":"Classes","text":""},{"location":"reference/tail_jsonl/scripts/#tail_jsonl.scripts-functions","title":"Functions","text":""},{"location":"reference/tail_jsonl/scripts/#tail_jsonl.scripts.start","title":"start","text":"
    start()\n

    CLI Entrypoint.

    Source code in tail_jsonl/scripts.py
    @beartype\ndef start() -> None:  # pragma: no cover\n\"\"\"CLI Entrypoint.\"\"\"\n# PLANNED: Add a flag (--debug & store_true) to print debugging information\nparser = argparse.ArgumentParser(description='Pipe JSONL Logs for pretty printing')\nparser.add_argument(\n'-v', '--version', action='version',\nversion=f'%(prog)s {__version__}', help=\"Show program's version number and exit.\",\n)\nparser.add_argument('--config-path', help='Path to a configuration file')\noptions = parser.parse_args(sys.argv[1:])\nsys.argv = sys.argv[:1]  # Remove CLI before calling fileinput\nconfig = _load_config(options.config_path)\nconsole = Console()\nfor line in fileinput.input():\nprint_record(line, console, config)\n
    "},{"location":"reference/tail_jsonl/_private/","title":"_private","text":""},{"location":"reference/tail_jsonl/_private/core/","title":"core","text":"

    Core print logic.

    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core-classes","title":"Classes","text":""},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.Record","title":"Record","text":"

    Bases: BaseModel

    Record Model.

    Source code in tail_jsonl/_private/core.py
    class Record(BaseModel):\n\"\"\"Record Model.\"\"\"\ntimestamp: str\nlevel: str\nmessage: str\ndata: Dict  # type: ignore[type-arg]\n@classmethod\n@beartype\ndef from_line(cls, data: Dict, config: Config) -> 'Record':  # type: ignore[type-arg]\n\"\"\"Extract Record from jsonl.\"\"\"\nreturn cls(\ntimestamp=pop_key(data, config.keys.timestamp, '<no timestamp>'),\nlevel=pop_key(data, config.keys.level, ''),\nmessage=pop_key(data, config.keys.message, '<no message>'),\ndata=data,\n)\n
    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.Record-functions","title":"Functions","text":""},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.Record.from_line","title":"from_line classmethod","text":"
    from_line(data, config)\n

    Extract Record from jsonl.

    Source code in tail_jsonl/_private/core.py
    @classmethod\n@beartype\ndef from_line(cls, data: Dict, config: Config) -> 'Record':  # type: ignore[type-arg]\n\"\"\"Extract Record from jsonl.\"\"\"\nreturn cls(\ntimestamp=pop_key(data, config.keys.timestamp, '<no timestamp>'),\nlevel=pop_key(data, config.keys.level, ''),\nmessage=pop_key(data, config.keys.message, '<no message>'),\ndata=data,\n)\n
    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core-functions","title":"Functions","text":""},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.pop_key","title":"pop_key","text":"
    pop_key(data, keys, fallback)\n

    Safely find the first key in the data or default to the fallback.

    Source code in tail_jsonl/_private/core.py
    @beartype\ndef pop_key(data: Dict, keys: List[str], fallback: str) -> Any:  # type: ignore[type-arg]\n\"\"\"Safely find the first key in the data or default to the fallback.\"\"\"\nreturn _pop_key(data, copy(keys), fallback)\n
    "},{"location":"reference/tail_jsonl/_private/core/#tail_jsonl._private.core.print_record","title":"print_record","text":"
    print_record(line, console, config)\n

    Format and print the record.

    Source code in tail_jsonl/_private/core.py
    @beartype\ndef print_record(line: str, console: Console, config: Config) -> None:\n\"\"\"Format and print the record.\"\"\"\ntry:\nrecord = Record.from_line(json.loads(line), config=config)\nexcept Exception:  # noqa: PIE786\nconsole.print(line.rstrip(), markup=False, highlight=False)  # Print the unmodified line\nreturn\nif (_this_level := get_level(name=record.level)) == logging.NOTSET and record.level:\nrecord.data['_level_name'] = record.level\nprinter_kwargs = {\n'message': record.message,\n'is_header': False,\n'_this_level': _this_level,\n'_is_text': False,\n'_console': console,\n'_styles': config.styles,\n'_keys_on_own_line': config.keys.on_own_line,\n'timestamp': record.timestamp,\n}\nkeys = set(printer_kwargs)\nrich_printer(\n**printer_kwargs,\n# Ensure that there is no repeat keyword arguments\n**{f'_{key}' if key in keys else key: value for key, value in record.data.items()},\n)\n
    "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 4fd90c3..d4a00fb 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,62 +2,62 @@ https://tail-jsonl.kyleking.me/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/docs/Advanced_Documentation/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/docs/CHANGELOG/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/docs/CODE_TAG_SUMMARY/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/docs/DEVELOPER_GUIDE/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/docs/STYLE_GUIDE/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/modules/tail_jsonl/_code_diagrams/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/reference/tail_jsonl/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/reference/tail_jsonl/config/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/reference/tail_jsonl/scripts/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/reference/tail_jsonl/_private/ - 2023-06-17 + 2023-07-23 daily https://tail-jsonl.kyleking.me/reference/tail_jsonl/_private/core/ - 2023-06-17 + 2023-07-23 daily \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index a637265..68cdc8e 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ