From a67ac42a6da68f703bba229f438e01aa3a02754b Mon Sep 17 00:00:00 2001 From: swazza Date: Sun, 9 Jun 2024 16:32:29 +0200 Subject: [PATCH 1/5] feat: setup otel libraries and initialised with env specific exporters --- bun.lockb | Bin 81963 -> 146630 bytes package.json | 7 +++++++ src/app.ts | 13 +++++++++--- src/init/otel/index.ts | 22 ++++++++++++++++++++ src/init/otel/logs.ts | 25 +++++++++++++++++++++++ src/init/otel/metrics.ts | 28 ++++++++++++++++++++++++++ src/init/otel/traces.ts | 25 +++++++++++++++++++++++ src/middleware/otel.ts | 42 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 src/init/otel/index.ts create mode 100644 src/init/otel/logs.ts create mode 100644 src/init/otel/metrics.ts create mode 100644 src/init/otel/traces.ts create mode 100644 src/middleware/otel.ts diff --git a/bun.lockb b/bun.lockb index a221f10dc0d68e7399e9727744506706fe171ada..5c89bcfc3c2743e9cb8f163dbc15fc5df29f895b 100755 GIT binary patch literal 146630 zcmeEvc|28J`~IP$P)LM|B6FE5p^OnJvu2s+%u`6Hh-65qNXC?+G8ZZ;QkfcPG=z|% zNl7aD-79;&?eF_}o;vjV^If0zxy~A{>%P~$)?RC!ea`a`6p;4y_L8=Ca+9`m^W(Sn zbz_E0(#73oySCU2l*nt?6AgamnJJV|JAV%^J8y4F5D0KUegGOw z2iy*McEGKGGXeDgSpiiElmldeya?b7KyN!YTO{i3W98%PZRb6gDDNcn?4V0dUfw>g z-s7&gI=R}RcaMmA&#B`&PHv8NUQRyVwssyr_3XhrM`&l~W95L_*AeZ{12TbKTRU$b zFZTe7t(~!C+Xx((Se|#xFG)| zZLKNFkU{bAvf3eO?QR?J9gL#-a41Cmwhr*ILtXMD`XvK}s5}{7P`qLRQJfsDyxn)W zQ7AHyLHX72f^?MuQ9r_=9l{_2Zvq*OhY}lw!UI?Y`B{Jt?&EP0fpSjBUj+Y=KbK*U z5WkQ>S34g^cUy0Ym5q&^hmSWU3kDOlvvGId?gVl0v+@c+@r21I3GXSJL5C0OQ=tsm z@wRjIv-6@*)_@bJ+yaaV0%`*yf4)K^SL@_#)bit9IMhy3{O zao2@%6qkeG44StAfXJSejk_0wpRy9_5q~XVR}fGP@`>OCvcm>1$c~G<+t2wn13HJs zX*VG9|2xzp-2nny3A_V{;!z5S?wf1^*8`&d!~r6Ig8)%GXFwF^9bi`g@H@mq6!0N| zF+voI2;@Tn(R^_f9`Dx%K*S5M@^bKo{uDwZK0_q;+G?9-7G1QO81O@`4IJg6% z{v3gJ$j^O%FqJ~>ylt#ptZeOUT%3HJKqpsxd_KJeMD3R^8uw!#Aktk0JQRl?&=#76 zJ_UrL(0n2vD@UP#>!GukjQeK=h;(v@yn~g$lRI?4*VoAwC=|`5`+)RYNy{KDwWz08#�FnPa(&K&{ zQy-6S1>|99LlXf}KRp0Zf4EkSkCPVU7eihU5b3FDj>lIX5XDhmYdkLoi0%(AB2Ojo zGqgkHX{*OkRC|0L?*T;q-Xw57AR4DJXvYtjqBGuZ0P?dSA5ZXGK@Y79jf5@*{6g`3 z27@I6cnJ{Y6A1Jqa4k{K4~Y8nLvlR+PXQ6Hh`@M2)K5o1G(Xh=MFHm#^#c+VWHR&? zpfI4V;rO^@KpyGLG8*^aAM%Jl&v<+sN1z<(8WZh~z}!Ur^)MNaBi`2rh`hU-jUAf5 zJG`tsJOU^bM>~IaG~;dU0$!_ zb3pzU(e5H3npd!U`}jE7c{%!eZwGlwI9xNKeDnY)PwAk3Hng{QaN~C z{dj%~ASaafLOmbgDzJt6(L%I8?=YShfX<+JQUOsrJ1;MHFG)LpE~jywkI)X)pNBl^ z4gjcQ|)Q+PhdeC_okK%d@@~EFaj$ZD*4vrL6C`acN zA>eZXeuf(d*;BC{FqKme9+T9q(ejjXpWn7hy|vBg>sf&oX>(C?ftUKv{Jy$*R4huq zviOopK!MT@m3Ma6TnLGOed@Z=D$z5D~rYjbm1E^}~|KaXR*b`yRhx+fA>RRI&U8 zbM%F_=$iQHGp0$hAAilw^Ss$%X>7j66$KZMZ`Wo;+_ZF0x$H2g30@;>d_!R3F-&h^+Vp;57%F@ocwKZ7ZYi8x8!4b2A&ReCoTy3HX#cu9T@)wg# zo@ey=%(H1{SU_ zTi!=^?9vL*IDYxM7Rx}Mat3F~dNCc<15EpMJMJYX)z0;FEuB8?M0~yA-rh_;-qDcw zoR@cm^#szZRa-e!%?JIyu1Y)i{!-+@B*q?vOxDCQ-;%fDYeoFM3#QxSVFVT|pFq_4$X|5>T|3G4>N=dFJ@oe|jD z-6NOq@QJIbb>t`S)1N;by0&{BU4Y^yR{pE%ZX=3c^qh_7eb6yJ;QmTZ{fqCw!?LA` z`vq@JpL=0NaFVS-l#YOYz?@sd$G*$#3~&{bJ#}|ppYQPZYs*LK?2K&p=G)y}q-aof z(%H{9U~DVj>FIH~u_%fu1z9Q@rwOx4R!YK zQ_G6$@5)&o`u3Z$!#&%e2xlS9LAt@73ZHAEE_C@G3pJndE$kG$#EvGkp=6O!J59>K9xW2nojp}Xnx8M;CvuaM4GhE*^ zAQb36bF;qmjm$Z*%aI)lsqC5PVvu+MlO= z&b+tl*4z`GUw82Hsq}KbDSO!G+&so}PvKG4qV@y6Ys7Vx>^q+%m4vN&q`x}5Kdy6W zmaWCE%ysT2cB8pUe2O7M%ACgQ{DOr1vpvFjj%q7<_@{7PnO&1nH$(c4fa_epv{`ar zqxZ>gQafb7Y9{;kTk{u{Ht{jX-+FXUYF4t}a!zX0EXm>ghoh#_6Q1{AVG^~ryrt48- zGpAa=b?u#qu8&@RU4tBj_jzUvc9pzypZ&c*O#kHG?1h#4Y@_>_GGBO|UAkAvg0KrU6Ac^|94 z9K5htx_#QtyGpl)JqR!@Kt*OqJE8&=Dn-DtKadL?(G%7JfoYa+GXhh)+pd3>Imdf3W; zL*>aG+l+dKk0*w-?)cVXf98`bLw*!f+;Y6HU3cm_$^LxN%G>!GNs0be%+)8JHi`!M zeM>Qvu;=}9Pv8~{x1-(lS2=uD3IXl{?uBtl+Kz+sa-z z^LBD4B`czw)`^|Z>kD+BDsVJ#zud=gXVbQa*4sqpwVKNq7cMS~;eKngV#V8wr%UdH zGrrOgwyNwf@6vNuv)*i*EbO>o;BLZ0`zfcQhAL~PieFtTv-{J|0f&ajS^W!7)+Gj{ zIX`eRsz{vAUlLyCTDG>c&*#yhp1Pm}Q>%j3%kmcev%?&!4X&PvQ+c(fEAP_S@~r0J z?-eikS9-6uoN@QXZkcslW>0F|S{omEmZs&cOseR**;Bh~^JYHFPltIu*5BRqa-&yB zg|Sl={aDk%jY_!|G3<25^0sLTPCs_ywckcLzL+5W+6Ug1V-Jm2GSxA@64BMKt;hHK zsiUa_4E&brVjtSyd2_$NWEkvnrL~~wT)k`T@JWNg)n_C&J~Ns1`mx&V=ZQ*{jJud$ zZRAr;kN%4Fo)4W_z{JW^p466b(P&?dDZe#^g{=-&mCJgxY#LW&fBXG zG%u(v?VM_}^rFAg-HlUOd!1P4mKb@P-IVB?vrAN8JBTC0+PpJ+gHfHn)y(2sj|Epw z3lE6cH?++#depmik#x4owcc>+U52+-CY&BT?iloFX2EEt{QUI$!^wPFP`BhqN4r_a`btmHmVppm&R)a7DSGRZE_v_Q87n>|6x4SnitW*(sUCPd zw5n`u8bm*O9d30jXW7(J8=FzMestH}_5Ze>$38v4HgfVDJw4mohZ}CR*R)^EWwS9p zl|Jyry3K-vacQo=rp&M5KZN>c@XwjPM_BH-;r+oajYpaHpLn#9x?#pOO3de~Y>jVE zaQnQ-G%4-U-iF+d!q~2Or{x68$FjDC_sr{$s9m(;#@L?aLit-Ojj#6J)BMC%l6$8znC|DNg2}(o z6I4GCS`O!PD&#rf!sUNb#_i(ZjXv;Edj`0W4lN1u+u)4~@DUf4(b_M}pErd<(FH!{ zVf>R1m~RJscm(qw{?tDaiF(XG4}496kIQJ0z->Pee8fS%PosqlZ7_cs1YCo#kN6Yq z{|?}*6MXnwM{~mVOMx#7d_;uLc{C@?e+7I6;N$#6{TG0Vqel3TG$zWo0zR65hzqVv zn4boGCc{J{%-;ok zHNrlsnrQs50-rqoXbfmc*!~dkQU7tC7A|Uo+Y7@D2amZzQ5g)yf67t*cM0=10w2ep z5eO6Qe+2MR{4sB$<9`$QN+dq^>vz7RI^6CD@Z||UF8dRg%wGnbM)5;j7z*?(h^B;X zIRGEef0~FyBg{Vxe0=|**iAHkb--8qi~diC4lgCepVmHL|5bsH?ISLlgR~^f4*@=T z{@{7^Ck@QM3Vc< zpgCcF4e(L?5RulogW6&~6&`Bi^=mTg$4cO%{v%Da_VJ)MG$d?49{6gc_~G(DN#J(( zfsgKA)OQqrTuv*03Op2+1U?#jkfJ$ZJ1c>&27Ekkkq#{h^Unfbi{Qia_n&>32>&hc zH3&Y^L}Nfp!uE~8D7yb~9^;=haJz8eYY_G)6aQPlC*S|H&S7ky0lxeq?;jK8tCIM% za#3us{QwdlmBV&KQ^Ncwz}F{@A1?os2ySN#hu%#DA8~Lwt^6F|uOspQ#KZO2&S&7` z^8>2G_W^GAZ;1IKGbt29()^j|{PhDqe3ky6`8(10R|6lu~rUnX<^xRLm1{=#v9riATZ06vNzBB8a5)_VZ+UjrYlADEAjmW289VZ)Un zjXy5`lLl_54}26qH14#XJ&-o$hXLP|us`QdMoIOU?+Xtd(Ef+U4$U806XsU}AH@&( zkK&KZ|D=Q4eFXmB_`ipfLb3dd{&xXi_b>Po@MXN(U+`Uk4?k7-8UM+QUk&h$|ANm0 zUlN=D1>X<&I>1Nc4fBBJgvb9p_pkk9GX0+h2R1dZkJjDE%s*}5|Be5tf64C!{)WGZ zpEm>ztsi*po#_5`7WioXp!=8BwG$089>4i>e|`RdsW9R7I~w@*g#SpF)`aa30w1ma zxZe}qKc(Qu73lnm^d@tD^#=YXl6_iZi2bhvz8vt;{6$<^_W;Zv1-=B~Kb`}$#1Qkv zd4HW>Xx)2}KIW$bAKgF5Hu4{j18VcTg!z5INAn-?(Ec;g{%iA5C`*8kd`F*uCK~@h z;N$%d=o7Yo3HaFm$*f;2^WnE);6JiGnf3D$@X`F6EPoFCs1DsflfD1B`Nz*cXxz}4 zPBi}7z*hqMunnW%H=*AG(v@#+3VBGqHQ8t_+>{Kw^_rvLaB zx7!DN{P}M(_8$OW1MFixv<}mfaC=Tc3PtTN{5J)@8Ss(K$=v_>z<2x$K9A7f@r!_u z_V3C1KS%i2`U{T&CwzVg0=_obrvevUcwCS_ze~8?cY=@FqBRhm0cc5>Zz3|j|G>w{ zpL|^YCk@=L0{H6SKOEzKu0OQSVa)#leDwJft$!%dx`$)FwCMQ!#j%^{`kf4Xw0=$< zH&62#itTq2eC+>3`_CsfzW>tdH{s@g=7~4((fUVg-2R(i0DScM5%mZ8K2iU_0e>~Y zN7^_J=>6{!j-R^t`15bne`Fh%|Bj35aJvBDqw^>3|3v*S20q%q(Y-T~gzb+2AH^Td zzln??=4&qczw7rz{XYnN^8Szfpe14Z^}yHp3;#J6Qz$OLM>Z$3{)YnJ{xA5wB>U2| zbew2|`(Fr?$A+*EM`84vbea<83&7!B4ftsNpmpq!0nB#+KKcBD>S#%rf03}y1f&{On;RW*-EE_+6 z;d^(Y{%;39>Oa=SIynDti0vl>AFV$)KT-P+fv*dE9J`6$|KjrF@ke}Qd!qL3fRE-6 z?l%i20lp0>e*e()AB!-bMe&z?^!$O=g!#LHkM}>so#^M^ zO5i&a{=-p%=7jBw!_8w$@Fx?$7~tCzd{LUM|5`sjlK7MH-)F`6^DAUp2rk^OKe>Y2 zRRiDpFXATxi?8Kh@b>}VjNmW&lhdSn?Efryc!i!{;rv9`F9RUM7WVV}hxV_D-oG)x zhg;~Uee46t&3}E1+x`H)F7T0ET=uUdQUz|i25uhw{1jE=a#GWOe2d%d1il7gA2r1L zz(0~mg_wU8_?jd>smX8OVm=-G_y_TE{)ww{V7`vl`1e=H zE-GV0l{6&Gj{!c~Kj;V!S_5cFm|siS$9XC(WN3rivqA7c;-`HEC`T793G+>XkNig* z{26K@{PVzvB`g&Ajh^AnriBb`uze-%U*CVB`*+#|`7yvp`zN9y`xA}dJ>bI;`e*#m z9GGbQrs@3p{EFg-`any<{a*omYp{>}=74LW^FJT>(1oA=Bh87%Zy5M6gg^Pn{zUt) zyk>m=Lw=(^2*E{5!u{U~{PkcTagc3B^oE9n`S*a2??2?jMElPIhbK$IzAz1TaT9Fc z75Mf9UmRD`fSBJzvJanQX-=5GWbH5iMQLXLy8jN7_>-~!5%_=8|IKjt!TZl-;(rJD z`20`ncMm8`IDW!NY2EwD{l5%+2V(xxdhd|= z3l07*ev!cUC+tsV{-Pg`+WrOK6Zq)-i1u%^_AsH&(U9=`IR|_^{}<7~!%Z+>&WJ+U zO87q?Drp@<%#Q)S3&Ed^eF@|7^&j;e*$%`G{sFOlFW{s3i{dw#-*4OlK8~NrA8eAV zv3+@yzx)0q5cu#2;Xluhu`b!xKfTB8J_BC{_$WX1pBny48Rm(#?*ZFrMeZc$x;KLU1 zbN-?^G|~BY3-~C0NN*x@5ZkBQ`0M!e`$ZI#khU!4=sHs`|ksMbpK3t{4M}rgXll4w#oM21Ais(@wtW8Ie_~s zVL3j3@cKt6r-x0t`s{@4CHkuku0OW+%l`cG>dF+U&p*1(^LZF2wT zJB;rih>ymARvm1A3-D3=X+8Jjpkn?dl6_o;+x!l3TRF#HpFdFhZ>`@iQ2aSz9!g5_b(4z6P-U-fv@ow{6)^= z&;KTC{~++u`33hIBXs4r3CFJ<_~`sa>$qc4%%8gb*ZBo$;BwsV-w?Ok0(=yIbl=c= z?;uUgPX#_cKm41me=5iPN5Dtx2aX%9K488GJbXs?FP^(t=bvnn%P`*-_!@+Lv<4s@ zS`y};20k8tWP76g_rS;d2d%y%|FC@#*YWt{JgpkIy*cpJi1^X!JK6pvfO@lKl@N_$VIeqE!R6Md>>5(fJSYX8$aq?FI7(fo}qQbbh1NHnM>E z{_f-BN9%oqy~F%c;Ol^WG=6C9#pSr&zu|w{d5nMlNBqh7uMd1|AK9W+1KU3ed?~_z z#GNR=8u;)C{6FVsTJHgDe~#z)-}zt!+ia)on(Ka@oxvdF7VO)i^q@_Kd^l<-(UCdMD_s8_aO1n z{GoLoV}9vh@+p3QH~#*>hevpm{`@ikd^O{63L{`B}hU1AKJ}Txjmg!iCaA5RLmXxLDxQfD0uuBHdL#OaB?sn8P0YGyM%B zI}ppCiHvA$V2}El{t0J5jTu}hrf@I)i~~Ft{+Y;#`e_3fvIArOGm#OM+rfp}IsL31 zN7O$!X8q*Bap!0H9isNIEdD$X1p*@7odgCExC;=CcPL!Q?>%s#^iPQD!+zHO9#NbQ z5bel_`V&W#BSdx&!G&}a2s}z)A|UcN2`*N+vf)DfT)0p|i1K-Gp?3LjA-k90LJ1+# zFDLLSAWCFJyo#Tt?j1uOjjYQGGR$CnJjU9k|fB^DbQI-hT`inh!7FLJ1+_ zccToXi6Dwk?@!k6aSF6~1sC$;9b70LpW#COeS-_>jKYNyLMmKz;1p`dh@AT`M0(Sq z997LA>L-GT&jvh%vj`my0yzmiG9tg{LODWyKxse&K;*wEAo70$ApE0jL@xxQdNU%A z5VbQW(2^+sCq(V6p&m7{A=)8C{-Qqsi_i%W#mSY(dl2YNAo~C2qxwKV_($1El%xMK zKjQBOMD^hW?gvB(Au5jnM1I5pqJAF%gnyKy@PhbBfT(||M0pyjgftOE`Wb{?7J=D- z$bK$?d4TYbl27D|2`nY>3LyNWT!j~;R|$yxy-AeU0HS%{NaXJVqV!LQ>^4C?!pDH9 zpU(i1ZY#kfBWl+{lp{p#x&Tqp3!)q$dfN>zNbe;e;`IZf_`N6cpNM+&cWsf*2!Y>- z@-aZfqlZd_Qvs146Ci3ooyeoVmy2{ch&&e{YLEV&FGx`45%v5;UH}lqOB4_#gy^j} zyda(=k(UN!g1iE%BoNtO0ePfL`~lYN zIjYiy7nEO1zZA0o;TqPGd~g4!n%<^P1Jf5}jf_$P>VWJKjD zM13kzj}Voo5qX5@Z3dA?i0U(m{68VOPtFka|AdHtmZ(RF-sZv!>Q5d~P79*;MbI9_ zuaw}C5&2O;l>ZJ3`?x z@pX`Vjz)6?<vp7@_*0QC_eu?PZR48 znk10^cb*=P13Fj#^LcuV4SlBn|DEV4Vh`rTcb%S=Wa53cr7H(ZQiVbxxD#yf?hgCi^|Dle0ET)nDB8Fg#}noW#1s@cwk0 z$~By{u4P}9K5vcT!p&E@`j=jl9mR*2Itq1bkW*_6ZJyMgOmETo=;DU7v$|e>8ZYO`zvwKA6V*ekd~e9p zyiGY8QiW?S&tJ0sUFN>4@7rXaZ~ABy^;mgV!I|D(POCc)niVfsOl!BZdPm<^wqm}6 z2xD%D<%cRK^z0PtqR(bHQPn6Pk7TqCiq3u?`_46UqbM`qC*Q>Ex;olYe6} zaKuwBsyVdhJYUnQ;xC0athVl#^xL2$Dd5#F*3fqzgs?99Y=;w-<4~1FNp5zdZj|}f z@Z>MARRtA$`xaeTBWFLOonf_))%WTx2Ah3n!N2CI8nQ0ZOfxhRc=jGJLfgHKb)bd~@hkkA4_633YH_kDaWO5{Ysoq;e zx$PFT!*Yt!gG|uF14a$f6=owoTw{>)imn40=JhNwYtnO+W4byc0g1V zeZ&qn-`UA+HVbBC?|G3=|CudffvCKR%9m&H9vAl9nrrIxr8g`>H_|^j2ZXS{=-CQR zRI}~zGoCy)78@|UDt+wi1GY;tM)QhhF$%l%hL;^y-Wwpp;(c`ea-j!uAEUq5uysFc zuy4sYD)rKRbkw`en5i8-YsR|h*#%D2oKr=meGfSMb5vx_JDSePTujpsn|X~hc2+_K zH=SfF%aQiM3$98R40iTPE*0mR!kv9hc(#?B>Xs+?lUaq z3Hrya_OFgE3UY1rnBIPdfBTKf&vl%N3>!n{C?S& z&e*&2QXDdopGlcfOzpeH(;)Soz*vnk0a_{voZH*U5^0Yh)2(&moASnK-M>jg;$-C?N8%&F} zD}>wzdLlLNo?oeAI875Ya2&|LkDwm@R^t?`YQ5po)|JuBpYAF8&FA0wS|Qir47Xc@ z?5oCvIrYxCGpAxegqZqF=E(9AOq zqte$g=8<$~lXdG;POI--Hdw%*^wNCe?nB|Sk*kWg*0c6Clqd@hT|e#Coij!522)`| zJ@*2xHOI9at=BvoYCF5$ccbzn4%NpeKag~}$hwYuM!4tqZfDr?TyO3%ONW{I3+2Bq zJEQqQs_6DujH2|FuX|#jPiI-vrzzrcfgv#^bZr^GsYNaOkqbfGZOkUTDkNQQvaY<) zg;S}lft~qzdBQvqg~=ll0*9G09!^!{Px@|}ZNR-aaP_;KQa+P8YZPj&V=|LA$S|FE zt|=PZUsn}Ms#?pP+-JO`@}7A0>CzijikDt3 zIM(%M_SB^3{p*&GUFZ)fIJ0*@-Td`SEa|N^eWnyNk^G%Q)_rzkwXpm#{(bR}Sr-Zm zIxg37kd3W5b@%0al|?!WJw7mpZ{G3EtAqc9`iH3}<~wa<3w(d^nAh2u(1O<1v+9g< zMM%1H$+|95)kiO_aZI}FGG(k;SJL`xQo+@BFLRFr=@tjl*XU`poMaC4`X;_t=W(Nt ziI_s~cl~RAbn5Fn*i5U;#I{e9A?eN|>*^m5yf+qUc(AGTb!AT)uiu>|B7M(qWH;*T zz87TtCNOK(=>Vn_l|#2v*%(J^wDHZ6DGVu=S99JCRA}Y3+O|CzZVt;EURlPoblT zwuB28?ylw&T6IcIzUQ@)s{WfnrScCv6iB-KWZkS9lPmi<9zH+JxxGp$hfnd2=Y58(LyYBm&*lzNuI%~{ zN`G!Ndr6a>rJU>KY4dka`LSZ%#kD^+?BlQz$}kRD7Dmz)AnP8taMJ3y(ArpX(8)PN z@nX5{F?~~$Qri#rFFlFtk2K6oymP%wzBxIde|_8MLnc`}otMN_>8OgDyjvU%uZ7cqecJn57NtUQZ#ET)O59(1`^A%#l#|xmThE<-`J8F}eWAjE zyK3S+oI|CDr47IGt0d*A&vjMWM)4XX=?ao{ZRJ&5M#>tN-W-Y2ILDxT;yh<)Xri+6 z%o1fiJ1h6DyA|u6^6uVn_yKE0C*K9Zy@DDm2lG9A@6J0aog&bXeGEN=#p}8dSvP-3 zq0GF^dh44_Cp@aytL}Zv3^%dnC?2FMR$Sw{&5n^4 z+pybahHYzqrHAnTgyU6L;o9Vq;L#UIayO^vSw5*an_23Xs z?1#?P@BPQ#zU$lC)2}Q?A3=#-;(k8DQc1CFRmnca)5BMjyYd7d`A>g#h&^D-ocYP? zR!`IFPbZ(xB*?nGIf4&oFBE$r!6>+Pqv7tz&(z0jm+W65n%HVfiR-uL+1u|CJ`k0{ zX<2EZ@F+GRU2=5#P2XV`S)B;=Wgo^8!b$#0{vX|5f&BRIrUy+b%O#Go(lvT*3R7Yi zJU7=;VqK!BgZ?G@a~`Te0=gNp-cJriaj3V=SkLh8>&$ZAw@F^QZ;$TQBI!zzbsx+! z9paCxuvx;-y_xHz!8ZGHzcV{G-~Z&XN4c(}aBXc(yVsZZ+`{Mcv=h&)U#~u>Y@^*1 z`+&B*L}mM1<|w3yP8Q*<+rb4eO_kfDqP|!=ivxyD{x2=UiKOnywbQtJ-gn(4eno zJ^6K}$Pzi>+7qQWuUS4CJd(CRd8_19SLv*fV=_!eS`GKYBkUe9nwUyTohTydE+Omk z_&iu9!pOur;1N9F@IwCF7V*`4ItSa5C56iwN*5Pjo*ULh*DDcfdTey=vZ%Hi4wjRG zojcUC8qB8T=&aSqYa!__CF`E1nsv?cAFHz2By{K#Q})^z*|wr}EQ+G@=|_xC(ygoC zpJL!Miy`A7@0g6VIoDgd(&x?B6KgekRUeJ^FDa-GCh4NzBjQATc}Q=4?#8 z6x;3Ku>Snv+-3P;L8UF++E-=IL_f@(ZSk=CHS4SQ1~x+8vh$q=ZivfUT_fqDvjk4m za~#rwicVANzH)T$w~tc!;=z-1qyG?V{hGmrC#r7la}vvAeLT{1%rrq_g!*+@Hmklq zjn!+$a=XkO7BMbYo5=H55sLy+kD5xz6eqB(YI+gbcHW@zdHJbS(XQepV-C!{Nyo%= z&K+3vZF9|uBM){}x~MjE-BY7W>k!i3Y*H9~K8N{n&lB{_498cAta~q0wO{|kNzTLR z!8OrsTDwH*GW1m3n!CJK%v@tD9Pwpy?W&+fNzoyf6R!<`unhOT*=!R8iJyf&FDY_0n~y(uR>WsMrD99g~T`i5s5 zcQdQ6?@+wks`lxsuDqhEe0#LPqeSxOi3ZBL_}=U&*o&YpX7 zTXWf~{OwFC@4EM+wGqcx1&abwzi3IEv8l_yd-BI}jqt>QlwjYz;r+ZOkrvZlGMUsI zVN!+d%~_kda6m&b-{^AsgXCx8RUgX>*Cz`<5m0NKhn~e@T~)HK$yKvE@~6xBG&Jv} zW!WoTjJa}bUc>~=8CkM&%>_i)#Vw>myCS4g@VWZi;@&58H2 za-JBRUP)g~Z`+f_B0V!!W9iUkuXS@jmY=_R+q*8@FX*xJ$D!6+yUO;q+Rjy3%zgg0 zf=0-?plk8*sU+Q1WZibgP{rv_BZIYuiiG7BfB#||C;K}6{-bAsu|YpHPfcl&)je&M zvevU9B;<+NV7Ro)=4m5q)%SC#F;6XUjy;y>MbbsjIB=qJ-eY92Ecl$wzHfKy?i)UP zhF0jb*;(l&nRllh)1Rj!rGNVUE$)RHF-_W;HjitT3Ny}DdJ|sW@%_oWA)T4QH_7uC z?fE!S`}^dBg$xZ{NB4A`6W^HlL1})O_OR8Oy9e9%ZkxI6jAFRd@gAwq_CwX>BeyMG z-9C99@^8v8DdFFH;)LuBDHrrv8_%=VSQL=D3&FHfCvr^!mcGZMET6M`=KNyj8waL1X{)0Xr zc|^Oq9_HrxAIy1H9$?}aC^z@YR8i~OdV;$)uUcmo|0w7R-Jo``mAB|8wIy{;?<<;G zHuEOBB;P!E`3OlDoh5Lhrm5{Hs_j4H{H#EMCElynVu{bLK$X6IYy*v32c}4zKfJu{ z$6(*m@`EXKCG8IaYt_~`UvZ-!F3IeB5K(+(z90I`hU0*qdErF8Oi`>k9VZ~%ZqX}s z@$`#iS(Xz5dnyXKk7jbKCbFlq>b;VD)KVMFG3(o#73ne=4ZRHAM{>0KORGXudUE4A z8bAo^qR+}WQNOd5=#@QxFd(X$qJL=L!J`*PE%&s|uU*6+mc8AC#r&vI!`$N7m)HI8+&~9Xj_1^&_)WS@7Hg`xmk=qP#3%^ygkLH*1IT z>rX$l$2wN7xfcG-H9e|XX(+>$U8K#jCwTU&k&)bcj0H;0O- zA=~7NuUIz1!TbvdH-4hp=Khwewo4PmKG#ELWT) z&j$mtE^BJ~JJv^gwp7{P`Fz`B9VKh^*%?=+uWEMLA{{Tmt9$3bi?T1X1$f(D?sMB< za`9!Xk$bLw)t#(@KI3@4wOX0(q&OIobrV3hwIryMm=!Ufn zStsc%3K}=jU3(|w`ssU$i{`0njcj|P8;8x*M5kOx@LH4DLwrus>Gt;lN;>x^vpK(}xZU8L8xCxi>_0Tzq(Qf%Ag&y|SCL&Ch&#-ND6bcK5~r zx7DcX?*4TS>}7?^G!-1cP&|B8>97m5czs>BQ& zVbgrutRIuDaiT?rakjPK@cD-=4ND^}MkT(aqBR%KUlX!!3Y})<6ZWdC{ZknBJ}Ixe z_w?YEX{-Z=9>P~d^0vFKoO&WeQnp<1B*%$2-+>8DaxN;sct4^OyUvvIv?)BKi$Bc~ak3Extv57}$h82VEE2G=>dV2^K`(-OqRpR2*sY(pdFE5D8BeY$KDo67MQ&t6rZ z4B&hC{`}?Kd+LvhtK0aT26leByTOa@uEeXY@7~MFZ63SQd`+^6q`QHvDC8dS z^BM&x--}4P8_Bw1fwyBny976#yONdtbm^vJ=Ra1?-(M$Fg(1L6}RHmMK9%5YuQXLWF8~wZX)a65wYU9I8zZhySI~4hak#QzyYJ1TGTkKI&1Buv zZS$6{$@Z6%_PM&T;JG6U+xzoWTeGEK=(J8PJ8ZCMsO;94!4(g+%r#6^_1~@9em0ra z;7BHi>9e(jtHw>~-D@T{ut)q;UoHPzSL>Ho<_LDO9-4a*H!zW5?DCxd^4-D#lE?Rjpej)2tHid$?? zu4~gjVlGS&pWbz8eUaDu^2FXwk}mp=8YgO+-jUk1Bc&|uyf&&14uD`;{O10we$=1EVymK&b#@8pU(pIeZDyQs-NC6>yADd%QK&s8N z`$6$#F*jm4)DA?|%*$LRxUi7Qkw0~bjH<31U7;aMcyWct67f)OGi6oZ^wh@EG%L%4 zo-dni+<4cg76c(JtZPBm{S@ew!6-Q?B~HC`t2eb36d)}fX1?ID~TQ8SYV zGL`P!^=cRum24DQ|7ftgeTt^o%q1@Lz z#r|56b&nsW+8Q07yR$Ft_}1vs)QC&ZIqtbx2iCmJ_Fr*$IBsM2kG(gvJ_r>ZHbzYu~%m=M%=4UU``f*{Z=>oyP{Bk9U zA->t%HA06sGOCodH(!!iptS1#sk>XF{R>479KET~W%xuhA zWxE#I8(Hk&byGS}qtcM69&_KKT9<$Q%h@>>_Mf)&TNpJj@=osSroat4&ZlZWb8ly!UYNOlOmMj|$y$9P@!gl15r$D+9< zv;9j$qDpj@TxqJZZ!}}$c>Tg4usp?abt{qvo;7--cW+zKk-x~kL>hv`S4a$uR znpKxa**RK8a~^N?$+{tb&pSjV*T+%7NaiM8e3iFKT%yy`DJn-9c6e@Ce4M0fPu3MN zj9&3F!$*TT$H?<==7J5OoEsdMyt%oeUbS<`&nI{JnF4`c!z20)Lef{m5;q4hm`O(l z6&?~8?yOup+x~T67D?BEth-@kxZ!xGdXf;;V7hM4r9k%}Z*`B*x1zUJd{t~NSedCa z^R&E+{jIcD89aO2^W+pRP4jg8q1yCDiFd9?NU$RL^QI$N_ksF!N8895v+}&CrHwit zn}<_;bGOGth{)QUqVHh4HtmF`?p?lzi%t#(N=cmN^;{^dqIYha>BbqG9!HFYNO6(( zV<)n1-{BLY3B66X^m(VYX?x~g`B7-3-5u>_u5$f}_YwxF^G_4+P}rXHvQFc8#=k5f zO>ZOJwyyg>_6wca8OyU}jVt<&8{ZesWZn4Y)Lm6=7rLxDZyLLWEPd22&3U1A+L+}I zhbI+RtV)mazu(h;Jj7UB_;bmz^{*_l3SVtFEA`A=#!M?<$F;yILnPhpWL=g=WAW>I z%2?jDrq5IU4DPbxp0AhmEFNv(QW|v7d4D~@we%tVM{mKl^yUFK<7U6U z8XbN1#^R^s&qL_D7o4a^H$5~zek`EeC2HB_=FeN0A|gHX9n;JdSxx17tiJMGyqUMK z`P|Ge!@G`t*W0ao?7r~!`TP~q-%oZ>%~mNy=$-;090yk{3P_FXW9DF&lCsXPRBqhO zaqMEDpP_5Pdoe$&BeKps!9_-Xd};Y~w-T0?N!OjLWgIe!H>j|)UA$u+X8=R57pqhT zNf+JII8oysr=)OxfApE@)60itp5^SEhkZ1LZf<4SlVe+5H&5X7N8Ol?bg`<1!>Mz} zwlxKcJa3x*&UD-Hf|zJGW33K@Q4qrZqBAQ_R2zXUQM%E+*FQB1Y-VixFhlC2zy0pf zHb$dL4Z0QSeD-b4rdrosB)c~HEAD06@^(wxj^amFp@#V>IUK?&+pePTSg@`K76qiz zCGk>N-A_D!*YW=KVnOy^vz_6|t}7MjkN#NB@#Ch<6Qid~cqMf#wA8ejZBIRjyjp*I z3Tu|Bh-SfxAd!!0$D~NQo@Ct%X6Zc|M~4MDmfn=LicB5~(&QLfoE3cUwY8K`({9<) zx*N`^4{haT9>g^6duS(jCi}z2j>UsZ+uj?l33<)*E`+3uKHK9&Eq5F(?hd2x;mc(S zv+pfoxpK+7b94XfeFiq+-!l$yUiMn}-Ku2CA^%&~>wai#XMfrCAt-T1cH)!~xgp&Y zE~yhBgyVqDtT<7ncHJ^bRjqAqpIKYQbMHmdwvjjCL5mCw4qD#2!s+-ZxMd6PoD5k} zi(!s3uO~_p>C`=IqWPmoOcrbse^7Q|@GB%XF-+PCR*=Mcgd7a^CK74e|6pNZiH4=0B zKMTnvtdaB=W8`lxX!*ib!T(jJiMz+mCrhcuY=r^+I~Gf{r(u85GcTN|^ump!m)eKQ z3Z|~~EQ!{l^Gv!@6VrQFXhq+3UV06M=Zi%`1FWR0UIjm0)_6=~ZG^nbsSc63GBFmS zAF^ADSCc=-`eRW*>YL`G22PbP3$vHTb)P%?M9ch+*vV$I>jM2-#T>oTKRZ8~#d~uL zyF>N&)yLjm^-$KoeE!B`Z_}0Hy#lrG&Mc1Z2Mz3R09m(Wi;J*ZmEZCc^a4&Up}so< z3k0T9`<}6Rr?u-nYjFuJjnH^l;OLNXzT>NFgqNXCx$MUxE`QS_YG3T6yxr@jk#qye zx(|-sZ0MWMVy!I{DzdKF;K|BqG4})DCuyHsib|Sg>1VMpwLJJ1P{aQ$ZigP_^xXa0 zqtnBtn6Awmd&8eRT*FF!POy`#d+ppoEoK|hwT#*4!_JFrn|r%nc>CP!=-O^8%O8xvO?$L%9%lvrb30JNrIci^>O4i~~7$%QTBh zv`xvMFLq&3Kx$5xp}FY;L+j5HA;w05D>qK(>8hQZ$|KfSWSrbcZO%92s9U~bow%Qn z_nigaqes`snQ&DGzgFlSJ@vp&R-Jh^Xy9=RChOi6KmD@5B7gC$t^A{B9;L=?tK#~i zA$~{KPvVK=5h-4_<(j1}Z+$Mz-LZ|)Gw;D174g{XGE6R;yGo7vl-SLWX^?c$8iW&7 zR_sZ`%@_ri`Pb-P-CZSnYPb6h*1|YRy?H^dfl)>Mv9p=u?yE5MKmM@qQs0U8g(eMb z)XP3?OU!3&XL@LC71jqr*xyhr3P=rd(G;xA&v*B{FlzL5O| zf9~xi>99jFe_LI>YK>g;-p^0zlC?&b=^Wgl^dn4X(dEn$lJ0J@E}MWsD~D>^?k2U4 z1nKWf731$e*rMz2{2}x4w9U0K>`!|xW_WDd*g2C^xwA{`dFTR-<4eyuE$pn1-2ZJ; z(-}W5lI|X|?yUR>8Ijb-E%ygMP2HIyyYQxCvgED{tm#~D#`65TXTDV`?5f{1N8+m4 znYq;2UzemEYn-Fged_qwzMFZ^6{{QUeK8z z)yX5-b@c15oxblTSIhUw)A68cXR7L4>r$0 z`PQx566jNXGMmd}zl8RMH}!C(Tx?-blZZ68O_(){q`Qx-tHn;Qa{F7FJ{^b~^HVgvFJ7@k^|+`7gVyt9_DM=C9xrRAcf_+Es~y!3>io$6yhfWTLm2&r z0LLMMtm`vS(K`KT+xh%u7qu6@m~(15fAsbPCf{~Te@pg^-=*kRb@JBpj(2h!Qq@DW z)7I97Wkf0F?)H51NLh(G)tn-sM$(NW>l#cCJ0bqTwV|K>i^cbgHE$o7vMD%Q2sl*k zaIG(j3l*pg>!^EUwewL_iP}$vH@tBpC!nq6EoVBq<^}XF))a zoU;VUIVTYb3TO4Ve{-wrp1QYc@BPQU`+Tgbr`FKZ{meUW_pA;x^c#^HXr8}_8{0=6 zdZ5{9mX{|r{_Y%Ynd2%E2O$p%kHQlUmdEKgLgW07Ju|WiOw!_0IAQUIz`f{u0nhfc zj^FTdUlgU>%Ui`S1xJ3(Yjj^}liwZj*tV6$PWyjlJ6wO-h=sFdo?m@$U{&@OHzB#{ zJIXYiHnj|xHx%xr_FAQ6u)dQ+j&i-hK*_t%*~Kl>N!n!BZKnEcP-gUlz$rOy#*i2B zyl}m1ID7TCMi!+waLCEV@%Mk6=EY;}!G($CP@N#*@x?H!7i8|~MwV91-C1nay$ zCvww`O(RO{g?XV|BOAhg+mK=1R&J<{GRd-e_|x=pg0NYzL)cjQkKLgPdQJcMJMm%J zWWSnXt*XQ7gA7<)=xy@cSV!iZWgqHCkyV!f5%O~!gOq|E;q0sOO8%>BHR;nziBao{ zD=1~eU#rumlZkMJoBpWw&wbCdp{v4<@qJS^F+!zB=QmdDj$gjP+dtkb6Q04ly$AEg z!o8*G!aFZ>+a4EBxe2bW{lvte7%GeDzCXAZS8Dc$hO6PATne?1pg0)UTk+l-F0=T& zepdSd<~pCZgo{C3N#jZ|FZ7v4HpF-o^Xy#|xx7X8maK!2L7WHP&pN&FKAu$)P2Whv z8ey3xo2THeNo*lFFkA@z8EIGpzO>)7Z0Wug|13 z+N@;0{`ZzhY78Q5%jTD-MGZ{Y-KSYKv)_m-zSq!nXqk!)Xg~kHxmByB_2hLDm4K(m z#`sgCVwg7p?tQIG$HcI_>9KnHkdxBr*-P{oHW!?1reCWuK>>3DZ@ujrhU-&JSh97W z*Gxt_%6Yezx~iAsEqL|O=e@jNGlvKBLf34O4e?E%HAGd^czh_@BI=@+$%u$3fo3X^ z-c#svy68u7%)Sr9xsN)ovibR}Yu(>S&*)>jTX8K&vf(AEHw{XlWfpV}gv!91iQ`|%76i`CO+SW+ zySj3boVGk>Bu24uLO_J{zC%hukMI&H!g;TMy`)SpKVQJ9g81h9=(jAv#&yGp9f`HE z)oDu1xb1{j6-4H!^S8gFl2`{kpS3Uuy@P1*#Z;mR&VnCLLf3kb4Z-MHt);T0B+Qpl zC1y<6mYSs}@`a7lyVpkZL|2?w93`>znDx)M>#2-(LKEU2qO=vg-aqPaQDVRum?A0L z$C&{lWW32pDd-XC<+di`{c`DYD~S=2O+Bv&^EtOTZL&sPD{RQn7TU$Qo3qiMvC`?9 zr3`xE6{c-Exw8=a*1qq_A=jLHt@?fu=7r7?kqvPiK)Zm#&3GsJy ze`Lieo`|^hD(JY=q-o`3(at@s1@qdG*JR>BDXXTApgzp|9`5C6>mv%e6%oh#^i}N| z)(icw@-(C|0V~~tvPouV8-`(B5<3C~9v^(RH)8LdtG@ix^f@lYo!m@x<0pq!#|H}N z91|Ju2e=pS)^i-D=$#V-*1s%IaJ1uTsq%hesE8izO1l^tDOb$(vyEf%e)Sy7@3>Np zEym<7Uo}XG)+_n;N036A)xfoRm>0SxgKP*E=dD-Xt?8O0H|i1_6s-@N@hQt^?kB1+ zzJ0tHCuAbp8gov|Jx+^lw%c7h&AQ2f(OK9ZRHV)5gIT<-86!XsM96qQBBh{5Afg|2 zjR&P2gzb1`Z`P{)T56(5FszIWrDhgW&g+nV_53r17dPo(SI!&LY_<7n+!5-6w-KE^ z93k&?Z4{QW5il=wEP-qY%Jwvjqpw9D+58WJ`b<(9HWlXL>IYeckGR;bI?Yqry~Jv@ zsTKFMa%4rn8^xol5@Ea9a05FfMA6}_rUBbR7Ko7XLf1f%4RQQo-O{F$^I08d2+VvnO(Q^%%g%~!Z< z>W10=7)N4ymrR8z)}bv^Pp)qP5z?E5l!6}dDvNAnnI!^rdOuC!!d@}DS~t4pS@ zc>+aE$mrzva%j03Dk?dpy;6U?MraQR{52Bj&WoybNE=v_lf7?#1oLLYz4d=zH&Xog z{nfdm{HAw%$yt#M*53m@vVd=~IA4Y`o)o8({8jXCIE_%db89f|t|qnkmo)^nyj}P; z^v8(f}cS_gO7X$eT-O=Ayd0)p8n7Nx# zB0j%P8Y))Fq{jN4m8o*`!9?=?O`SS-`1g>xaBrCqC0}DfQ&?R;!JOea0cxr^o+hpI zu9}-JSK&jEDZz^uk5vk&bnL`dbM5*uvjvst_$o%HPy}41h zWBw{kW2{g*YCjm!mXI+OHhYL3(mi)e{|vR_2BRz{E^bB2b*F3IxXcFby!JAtm{>AG z>P;kG3*JK*&kbPSe7JWcTj&F7Ihx^4u1ujB&8}sswMAupu33iuy6>xTUc~uBn@Wzg z4k7ZJgYj$>W*y38e}*EJ>#h!kVJOfkwTJ$Mc?;m)TyB(vQjWLXLL#*Iro|3;zpv4b z;BI0%uLKVSZ`B$TadmkbrYyRMCZTq4)Yemy}uH* zSo$}cy<7x!Z`T(LIoBEj8&;<&B+ zzxdK2$xf}BOVD>A$ov+;y&PG@-)YDgU1?A~{;H5Kd|4z7VkD#Pp3SaFsd-cH%6OGs z>089)NTV{(_1>?EjL8*WFv7s^A|GLu1-bWCG!nqP&@l?KAQ9UY$Cz^k}3E7|;hK|OX1h>&?GMM^=B&`hvV%@n>;Y$(lG>zcE8ECRJJsRcvRJd{*_oj)Jpop%M+Km5LFDYi~B@fqeVgL}2vb)Rt+ zPWKbWL^Y{u{)*u%*fFeaYf5E{xo*)R)O5>tcC%g?Cu=fDwdBdOv_D2aAGkjy{LaV4 zD$7?u91;bM3CMWM;oh(V#)hj`Hr;Iq*V5~PB`OjvDFiq;PEND@v=3*b($uDZYu!)I%7Z;*D#2^=CR<@S-7N^dYBJ(jxpcx)7bnJs{2zoZVeB0AU%$vdJ-)IvD z^P6Ic>C&anRWsdI=vQlps;Hh^$E6;|oEx1YH=t$}&AHlGa~Mdd_^|n&PuKmG0{Hj~ zZ5zmjIOuGbxFuLb(d_-o{xGjX;G(d+!=Sj_sCJW2Hp}u|l$?*Egc{?7!u@s>RGkk* z{W48ue8pUrD#8JJ_up>eIRFtdzcolH=n;*DR(;gUm#@cszmw^pzblxGVlTqK?*M-N0Xp|YHUzWo=^EZo zCV3?ZBF+M}h}~aQld0FgCfA+TS7T4hD|@{+I(c6)q)}4qR$Ickw%@fNVEyT0{KXZ| zW}(hW3q^4tLdN?ADFr=3{bWTjWFCV5T%)HB^=PF3s2^f&b%~sBDK;6XBQIFmD~)J1hU%>&Y9}O`i?Bw)>QL>uwCO z)u!{4E=JUcbuB_XoOy^=<%WA)Qx@mi+N#v+L?-7avtqt4f~BLE_qKRAzQVlqa4$-~ zZPJnAYIJ$IEkRLs7?s)?i_%qHlsBTkx>sp3yKV2UNhQs{(fd@K!byWUH~1Be`ZINn zp`US{UMKmtzR@RmJ;Uq$Qw_|D zIF%;m9tM?6`S^UUO$CI6W%;@ehnj2BqLB^!dj=?WWJA0wU&Whg;W~XZ!FfBBl(=;4 z%h%3zZP!poyIW+uq`siso`X*%+`WGukj_fgqE&)^q1FcyS^=t-D z`)2g?2^#8JYg5z!1Xm7yH_Y1v_u_JobPKnW4>rEtqWb=d@4J|ywV++6kf&Vge)<;I zi|Ttl)*>QUr)1CS4+)+XZpk459+qO})0wPQ2xJYPK<>B;U>Z*;{*aheZ(WplbdW z*u}{zRfua=yvZ1nqpvdFgl?TcWXtL?|WBr zjw1MhQTGLn1ivV@_7`#A=f?Wy*x`#r)(*z!Ef=NB*RHq}8U(iTR8Rp2G7r$TN@PRm zwK1LEVh&t#u*{gej(dDhp1Z`d_I@9)Nq!e;giW%sIw5|5y?|yh$w=Em$UZk=xD9Vf z0)JX(IF_Z9P$;`75Fx#tNGa$M?Z(&Ec^`LHr)>ARp})FO7Q5!sjjuBEaP81RWPP+# zgVvBnmGy7qOYZJHq1%2lmV_C6LA5u+Eu|Z4pb?v zge9he0Sd|m8)_xGfssdz*N*1z7`QO;c};iD(5m;>Sn0e>*MFq(AYSTK4mPUdpE5u5 zA47Z!_hqw&pld})Zy!<$dW0v{#FyzJ$NY*nxY*pZ%0yKcZWnmfk9+BzlFZ&t(>LgCjn2H{@uf_ksQOg>vOaf@yDn*=D93AZdw`~zhs@~@s+eTjaw z+)6xqd#1AJt~hinJRP!QdGb%bgk;=IC%^0J zH--IMT`q>FapZxBY(DX0n0Ey3B`PSf*iy6cTh(xrZLGJ9d|pKuEYJ}co?Co1EOU)Q zvX^bF{x8cw>f)G)rLs{t%F**fHCny9-$R~n1rlsWi668kH0FU-8s<5>u>VT5yT(YyF?3(J5FHDxN7S;qh=uymJ5QpsM9|et~VNZxM0sFA|4af-U;* z)u&^B3l03##)o+);NH>{OD`gZ+9w`=Nsr|Y1>7%~=n_evv}^Ix;Kge9^gZeLNz}PA z(eHaVlIBgn0?qu)gB!SF3d;(vJ)I*#_cs(^UT9y3YzQ6ly`JwzwQ=RPvbQsSNJ!0h z%4W%jYbmzUvXWDhvbQL3skAkkOMZDk>1=u3+C)=186#)E*c$)3cCV+!{fsprLgoSb zOd}h@nvlp}JcT6Vc8<|*%c@bE5PpYMfMZh_U2us>g7Wb<m8cU^-mp+Rp2O^|*3MmCWqJa=EFB2nQx8%X6+QcW%GM&#T%#}0IKKI%E zcyCV58)TKdLM|;tTY1sE7k&DcHF0D^*gi4Jy3c|8S(uJXRO0jFK9n~sdBcOkhfSy4LTi+I&6~B^JBHW; zM}_y+j8hfzA6V*)%@R10b0j!DJ$YgC0)8EQ7AXZiLVk&%f){h8+&O}l&PL!P0~%S3 zRkw(<#_l!D%p98>#p^*U&jU8XyosNE=+<+I3pT2r5=g*XRihLlI>x#9)s7jCi3=MYm=`@6$<1f-lpj z0@K&%OY|Fc%>2+8hRg$WEg0DlsMm#-Ej>)gUOtq~_mfQhemeYmQYioQ1;^V+9REyG z=6;*;8c|a|&vN4hYcXSsio(z-=d~Y!L1yIN8%F)i{s0luyMUB}9%1pRp27NFt6Fhs z0pB~l=R?isr-&#X!Hla?DAKM5R>LVWN_4$q37a`~UVm`hy%X_GPnmjs^h(Q5h5D*SjqiFdm^U|H^(^3Ijd@z_9wjCTns1wA5=`1?V$)!|{;$2PvcWt^jGi!H}j zySRNgw2U?Ta-H(yDYy$q(IaeJ@Z`r)kmmC%}tvCuU;>=3Rz+DM+4| z7bYB_zr~wNmAId=dB56^w#{mAjhv`WFTUvc(-HOw?KSPhwsW6EHio`6B5Q^xm}aJ> zA2pv(sa1wPQ-^t>*FZKz-3_du0rDSKBto=TGos$SrTOrVvDmt?LEfC__8;%MErRrN zR{H(6F$!kQ>=S4buRdKYlJypIXyOC8TyOLskVn?ZdV62*xG$7~L>830dYeDe?t=D(6^J9$G zi)!+B8@n*?Cfut+zr-_7G^^Xk%amV2^nfZRlme&A`Cv05s4?ZneOnM^lpXqnb`8IJaY%h6SpO{aT6(+cMI;-jt_aEIY>Dpko?x}UH#qImYFNm9nb$% z+pN62ckFog_!<3>?dly0d{NnhMVgxrdxq39^E@($X1$|NADi4h6oGlS;oi9KZ+s%F zq~i%BZ%{f&1u0&Mw0f@I=B7iaPGlZ_jYN{mQXy?bDZJ~Ls{bgsW4!k1FrVmK`w?&E z)&4g8+UM}?VF&Jg`#SdzzwL{Vzl{yA>kS$nFEMXEe-w2*ao+XH@B!^3+0nyXrHA_J zrKvl+2l%34Je5uN=0CGa9_q`vpw(~nJHq0Ha*b>VN!8Jz?+KDC&+1-3Kpl_@*kt;M zVNJDFr>E z&?R!P`h4XT{>PU$PZ%{k$2!VbBjy}Albl$^+{C0c(0X!}3;1ri1#TWVJr-#((&h-0 z&5l98wxT1=wktXs0vyQgcOULWr~U53?-QIaJQN=NvdgL=b<0C#OrA(+zFoES?tRZe zVwIS_8|G4Fj7c)T!bab%o?5>fSsi`q`5V`mPa$ny2If70dkJ_L;%PswOylz;e>C*D zE1f^}o(xCw+}>@CFl&xC^iwv+>o-=j~nc6woyX zWV}D&UXm;BJY+kJ^RwHHdWdiL4GClLimRef*fDgXF@E1BetZv8WrFpkDlx)8V8Zz~ zUt%-4XPn^E22;G37d4KP2>5xxA>8|v!GCM)@94*-LCS}7>G>=UT5n6K%9R@v<@D1v zOAMWww`t}-jdOnXyJLw>vt2asFx<4|@ZDIgyT)KPw}&7PEZ$#mukc#EkQb*c6?v{= zTN}yQui?AN=tlKb@>cY^vP#KiH|Phs^gW-`80WIPlW*R_DAdpTc^rb}>bGBzlQrJ) z9XeM*=HUqL)mwPu{M3S`Mk!1A;+9|i+o35Ghg%ZEr|6MFHL7FM>9?KlK6JwUZEE|A zR&!aYz`!)^xnn(q!Mho6&V;iW$XcOyGC; zT%TU0@48k4^Pa%HTQuC=Z&k`~iix=n&rH#^+ItJho9}&V8B|xoO3B<}C0ti&6KA#6 z$czm}L@Lpi8B_XX-_X?&+fyS^EF$B^gLzNkUKHv~Qq8?)IyRoc@t`X+rw{sYoKu$m zu0AGU(c7aCHPV_nkPbI(ZS~v#c>jh-M6|%G3aaf!tGgTo?Be`ynTTLsXbeI&#F*DX zHdSWqAEJAewP;=iR%i7A9!X(X@72yk%egT*W?S9g)HK&c4_Yz)`Yq2x9MrgeOj&|8 zaL0FoFG6-r2_7$W?1OBGJ0={@W$EX*=e@?hH|#NdiM%78oHfU-x&7Ex+7z-Oy3D_8mMq{9HxYS9)7AE` zzG~T(^EsO=+7;CN@G>INCL=o3+1q!3Y$o}@6O${)&O7YWlfnl#2vKJn{Wj0A-T@JE zdpJi*L5~R7^`~5p_ISqZN8~oAsHvL4?{V-sD$?@thn01Ykp0-1iSdcwlBxoO+4MA_ zJ@k9Kr|O6*YU9B>+kYO5ok(j zX78DQ+N<^1lA8~8Y2LUfNI7xW;HrOpKFhM^kMS1)5ez7Cb}o(u!RV9ZDY!MWNY}8-5b)xst9f~DnV#0(tdh&T(grmx1&3o}sGlm}P`gzTf zN`|mTx>hD_aA~{V2P{JWKyUGemk9E?QfLfA#)}5`CRQ$%laO%wV))Ok zO1L9;wDi(#CX9^6yIjlKessaEr>weK`-*9F?na6MQ6asBYn-Z1YKxR*b3 zJh8=_x?cLlA+?XPJ0E7xvfg2G09H$unqsK`?@p`7@{<~W5JJ05O8mg`-i33LUAL4k3LWBWgSPF^j%M#n5u`F-Gpy1px4XS74#jy z_B`Dbr)hrVUd1xM#Kz7h6?fIOZi1>f>8&4pKZFkVO7^qOa?h-GSgYo2T3yNNO?^n? zt*V2j7ANI*>kang>)V{~MDK%JFb(ufII>OuVbB`zYuG>Lz(vv8pdoF11f6Rm^NRuZ zy4CvMe16ke%n?7%QusMu#F*8{`{k}wi#xA)Y=j5P`?Efi8{O+#wd=BMk3aF;;-_|I zezy0s!SNd5q(6Q%3$#x|dNJW%=csGmUN>2P^atQ=BP^|B(8AUD*yqhTPK>s3EY!)D znH}pCctkf^gE&Ll6J{5VZLd>1&!;>sZv3P1Il222^cy3j7g?VTOhQ3arKFsFuC?8- zPdf6o8;W~@^`tY--`Bc`MzF^(i&L@Y-LFWE9{H;9T^ZvRe(SK4jB@)MxAPqYQ+|6! z3^!vM!Mxb;cwIgh-u!BCZ0;)Xb=0`gpOn4Rz0f#ho%0vgpnzI|>d3+O3Rk0KrccJP zPhY(G*k9CKnWb&Wt%hQ7Bxai4B=rO4y$bgx_&nbne|yL1hMztuD(|@tSpzl^QzFrJ z)*J1fpk$KFV3e1ZG_i|y7isJao zm8dFSf6STcmR#zcN2Gl`DcP}Slo5!_D~U!_BU>ZCBUR`WI_^C3LFmlY`qK*I-YXl^ zgK_roUFaARnO|gGJ1_|ap~dplqjD-(!|j?(%4wSK87EFSYeh_@kO-RMVn_fQzV;xm z*QjA-rO(kB>P~4|z>$~2>4|6TL!$2TmRE*sSuigyJYJW1!n`}|zjG2yS!IRmre_Da z1n$(Rr#RIsBa&mD29*V7G-xb3E`1MDl9HD0bhc2T{&b2;hpVv~b_8X_SuzbC+4ra&!SHE3uft6WEM!3mtss3{9Zj; zWA33^l;QKccxEJ;L4g36`eWjAhk1MI2-IHQAp8qDKzoHy5tSKi~ zBw<%J#8x_GM~J3C@@b3<`hyrgiiMkaPy)Dti7Zf-h_ z58Xqk69o<_F{Akz%gLE(Uh3&VoI4N?tM{)`%8># z8%^VO=gLWwe!bRX(RQwVZP2+ka{DERdl6-Vj}6rHoOj>WGtRPAn-# zjZp}zavSq2zjDTMRzy+}Gk>L>SJ&7vgTT(xH&21T=_-H6f-TH@1MZdae8lzMJSDx2 z>c-qJRAU+Xn?{%bn_dV<^1hh$O~1Cr!o1XQ?+*?ixh~(R>G4{!H`cDK+Fa_> zRVN=C4;lwN<+w-V#I8`)Qlcc!A8E!T zijmp(TYV~RJ1j;5iwSpJk&Zbp{T{iJup!C8dGorGnAWYa>-COHvT?{DW| zlokpmcV3W7;>@V}_blzUK?_zaAsg%gJR>h6U6_{^?!_SK8NjKUpd?ORyxv5U-9_9N z3eGHiudDrF9k`k_a?;F0t@+hh*1%`+3$F~V_v;KZy`MJ|UnXv**(qlr&|_gQ)7hi~)_bocC%+gl&?BEkE{6En-eqnUvz!CAx;`p^1=LYF_r1a!F?fbA}#bMjy3h&MD;gy`p~%*a{Fb3 zd*jJQ^V?evuANFY+Fk2QIxerKxi%d7SZ)I)-b%rw*jPaBs^xq?`Td#7XHpgde1 z%@ot(*w&kEv1dYD3|_mfb@1CNQVq?sctAQ&&o>8ty@VO=_4{QKeq{f?!fUc1@=2%9 z=WKLx!Bc_0${Ro0tPVLS>p2i%PuRI7YFo4CjHUk8NZgyct}MZzt@`k_yh&DZg*q$` zEO2k*>Bi>A!L+^(KdVMHET`^l@{h@gL{9~3_0(&=-EVf7wtJO z{ZYdx;#%y~d{r0zHLxN5Mj)^so*%L`3Mc*C6gtM&7QN};dG@i&?YA^PhSApugwDcm>^1(zK#?u^V5(C4*5jtf z2$+`(?mcV2y2CGde}#eX3f;|>{eWhLU?*w5tieO&ixeX*;ir$dRw%ylcsN*ZbFs=F zm!?i9elv73m5W+>Wx7VIW6c2b-iCYYzX_?v8Tt2G(vqMzQIS6I?W#6-%3R^2sfkOs z8auagNI|G|HmbZ7%NN_tBVBx5q-=o~R=vN30rT>~z3sFPT@-77qFKDt z(_dPakB^ak6fa2dx7GbRRw5(Keo80CJbCAahtSyiDL)lzoU*hOw)Rx}Z{8bZH737x zXyNCZ{BZB~pxhnnG!k0Qi74BvpK}UwSEo~RBUh)7JXbLGp?7ebJn# zk7aqoC7%|XgL=t0bM=Pv6bAl4-#sG7UjewcUtHv2w@Ze|75%1$n(C@eLBB_%Q!M_S zW{GvLDOwkprMPT_u9uC+kike7)U)d(9Gcozu+- zrk!3t^PyaS@96m1aRVn|5=}j^cljl$;DCuIG5bkV=QGQSpkhpmr3l&LYTY5X{s#_Z zrm~|AW)84;1>xR_H#;I}m}rW=B@~@ZM`*U8a_x`4mwpq#@to6rblxcDe;Cn5t2>)g zmoFD6pmFO<_9`uk9J?O%TpQAOD)hXjFb6%*j^+e zt+b)_0mHS6GG}A{DUE{az8cq9J{|FVb7ri9$@x9u&$gHs2aaWE&~Lbq`4xtHt(|{8 z?J5bBUHMXuem60;dJCV`oxAt^rxIDbwtutylQ6`&C}YaH zBE4(h@l%)Lzl3>3;NJT~>Rwui=~g~AdNwH>v@eeXBzDzZ{2mWhC)ys%3BD1rPQL`j{M80lrj;W~BzMmE56@`0w#~ZvStQf|)$wD=?`uhs`x5>0` zgs%P}&2>VZHbg2Sg#n(Bg<>jX9F?JO90vD54WVGc-=b2jtM2%1$Z?hZOh zK--L>qIe>6Edv&>INV$E@yXaKwIs#ygU;Jh4$}OhPv!;IHA!Tx$BKq#UH5CA59s_L zvie*%G=A)u3`jXSs|o0w&Q&PpRed`p*!BP}uK(YHs+{)O}N;tEXY7}RkaVtgu2 zR*76|?6SP(7^B7T>#5Rk@6TP$+?5|>B3g$_UOGh)KfV{x1yjrPOi>JA2~0ks^z`vJ zxHX&*L!zwbpb+T&bF5i~9`$|RkE0e0+~$Uju4!1jGH`GG3V8rV!*z93cH!NCd;9k7 zlWn?ts-Cv)+&9tEgtrvAImUA)%468v$MJ~hPlA2&3mprZ(xsG440MFagz))b-urNG z=GPt*^Q{VO-cka`gT!!pJz3%rEMnZa=+fjoxc7# zqtu}xKa#O?Vw;o~KCa8cy*F@Q>rg0b^{*XY6En$wVjepyK=RH+cELr-Y^B!x9KX6@ zupNCrYdmOSBQyS_>k5U!2x}A%k<(iBJIc}<#?W_7$ni`L?!DV+iq^VoemkB<0BcNJ z4jc9MZXnTS6>(^cJn?97PJQ)CqeZxvME-re=8s&FkVJ4xTH{nqvu=2d`u`>hw~cQK}DiI(W+ z^kbKM&*VL-v@06S8Mb_=fZxacE+Z}-mZ`;%UXjUS1ZE3^w|qmufz@6w_}&u zE!!KvISBG73uCYdXgj*%{(1FUub4j|*YE{%<*CZa#e?{qhpC`+UKFvkN=M@(@V%Q4hB5fL2> zYs*!3Qd99IroCIJ{RPz0`i=1KMU>#)d)TWjQ|}v$Tj@H^{G_K$S)=#fI-EzWWGCDY zp?tb=3oSHzeoD-pNc(z39O6@GWX9;IK;(oPmOJ7n{Y|<<_<4&m+&eD5Id_opG2&GZ z*&XW*szRD1RfiaqlG4?%V)uE6#*2kXxfax`2mV(w-8BjtYeNR5F32Vix0+0rH;+D2 zhRwtBpaS=fMX6Py8-y|Z`r zwgdYAM9BqKUcr6fV6)jM$evuAJ)t8yqju43tWt2zZjj~cE)gtVHMp0*|L)z1--ZJ7 zwC{e!eKsTcfc9va;GX;U07KL(hfhT=cp9!2=0%Ym`hR{$%=3^+B_V<{Uog-9Azs3l zBvH=kT9{WI?)6yrP)^UDEjA**ACW_xo~WwnRlNTodCNy^j?<@ZF*@6$Afevj$&jiP zhL`mtlqWB*RhsdwSy4YWqd$-7z6IaTG~izE7g0yAd5^?>UXy*jH7fO6A1CpH6t>db zwe>E342+eV+@d89*~G=E`s9qJ{yLaQoZYaxMn{92Q>fW1h|2Fd4~th5?oIkZl^Jf` zX^A3w9KkMi>Pwp@R~q0r`lTXxieFz}SFZb-p|wVNJKMa^Bi(4;9(~c~IF|9lGuH@8 z>K1A{Z20dSwcy^ix}IbS?0nQRRT$hsksbAxge~=95)O@~4uP|8wi&q=&ecYvBKYDMYd+9Ba6iWfmYpKqyN_CRu%1yt$zn@uRR5n%< zTH`%wXt~#!+-T|Ep=g_Wvej)}cE$RF3>L2*+$$f=YV*q;QA>1uPez6AF%;QA^;HUa5 zJ+JQS=O-rM!QwT5drwUAN($@eoC%#RTWrT!so%OV9-s!MZ4ww#L+loVC&!` z|7&?4<{om9aAd49U!;GsP(0or2!1#8!i`NlQ{&G-9$_$13Z@PxRv;Ut)^Sw9PmF|o z8&`eT5D_bF=r>%*{k##}OZTKukUn`|d0)`^)X?W%lgTrd^m1zLLc-<7C8E=iufG!l zGE3R~ixbkE4AadUzm_F#EJSbIp<4o-#X?>|jDV*hNc<6GdmL{7+0U9zCEni+@@~a3{Nex`(=C!-+BTb;^wJgyS zrYkT(^v91VY&YpD{QE*vxVLh*w;IL4i(ZBE;z2~gb0zuZ+{CDf7n>C~cTT=_@5k9t zNLTQ??D#j66Bpzte7PubnS7AF5HC;HYxp*Z2zv$=uNmA+w3h#QIB{R&Rk@$cvu#~| z`C49fK5>c*mEV|r;zUwZ_Y64X*yjkf)L-cNu7{DUdfW#02IN3#^i4)NTr1nkg?Y{4 zUa=*zhPMwlXZ;dZI6`#fw;5^K{uG)B)YsX!vAvr;Bl@FaNsiw?XxY2E))IF!$2|30QC}&f+i>jJ1?^C!}X06u=Uz`iGmxsUgK|o~h z%D2Y(05@zzeuPZg+2%XC-KzuK11?zw?4svW4UL$IDk5wJHv*CtR*YD#3t>=6?j_+R+b!8p|uHOorvb()RzkHwQ z?WZfb(vCf^uRk3XWPl%+Si!vsYh1DKC&`L2gAFl%IG~}Ph~?f!xpl*xqa6IYb?S%j z`Huw^iDsLTrRp2mNds){TLB87{B;`48ri2vf7hVtiR?rd-CW{yGx zE?naL56vI+K8#K6o$Np_0Z!Dh|A)Sh|Ke)=w_FTb=go{LDB$}6p9#>@U#|0i+wH*B z)yfQd^N;y2_W}Qw8^Pt9zRbX71}-!3Z=3<>cq`Y0&ieo;REuGi+E9C#T0M-Kc!4{d8G z{~t9d7E%)w6!5#Df31(pZTvsCA`t(~j_m()OJC0Y-^l>9?OHo?dKf>mMS=by3Vdhl z^WX9Pcb5HEELJYi&#h3Zzy+rNsn^K=png-Q;H-a932Gz)b9)f$Xqd>7j4(orWf%`V0#DQfYhs{6JpgM+7 z{E)-;pJ`BSmqaihH2<@IrV)V0DPS7(oZUauX#OemF$K*8+Ep=EK3)m1GX9@e%fDiS z!iVw<VrbHJfXUqP>oDUz&(Hz0IKN-)n9~a zCCUQi01p6AZ9k|E-$MXY-;M%s6F>=|0#E~T!TV1JqySO@X@CqsCg3$75D)}-1Ax{S zR09dBX9jKKP(5*IyN2pRkppf3z&(~wC;?C{HEO^u01bc^KnDQ#@J3+(fZKVaFae<2 zJS+fK02_cEzyaU{Z~<-udO*I50VRM^zze`jfG5Bk0Nw1-7vK-D0oVcT0V)7JfIh$g zUI02Sl`4R8g30H6af0GI$p@caXSJU|9;8^8_V1@Hm*0e1kUAWo>h z4)j^A1kizLi~uSCIe-AL27KQD&45}!As`nJ1BeB<1E9KdqhR@6z&^kXAPA5E_yPO@ z;1-N1J^){UGC&ic1JDD=0OSBtfUn>+8v*5j3P2?w5AX?)3CIFu1A+n2coG5#1-u40 z0~`R304IP2;3>coUPumCs%`~jE) zEC5ddmH<?dWLx&Wxo8dM|C9?b6u za0b`_Yyr;zZNS+MXa%GLVgS$>9u0U7AOy<}14aO&fbW1bfDr(ywdMjG?f?%!JedD2 zz#TvYo|^&80t^ArKI|Qso&@j!OoG3YLC*sa2g^PHn1ja+pojK9AO3j^)wMGQ#DTv} zKo32i_|J1Gpic#0L4!VMVjlm||G$m((3lD37W$lCfMuX{dHg^00scs4tpazej@fC^{{+iHhFaw}@p|O_< z0KIo;erSBA2QUDjWs&nj&q3ogJAe4~>DhP-m_hxsKEbPVZh9rO( z6%>$DQ90xgL{LN!MM35G1Vs)JMNqi}6htmRLGc9Nuc~`?W_RaUBJcm+KicffR9AI% zb#--hbx#kH18E>qS0tj>1?heyD(em;EiHm~M$*!uXIk0=aNh^1I}-8Q4T<0nBlSXh z5UD59Lr6W4NPfMM`Xlv2>Wf7A=w7KK@uKBp2=23ysBD78^P{bUQ^I}t4I%}Q{77D; z!AOIUJV<#+xkzp#7g7!q;Sikopz^33y4EW{U&12zjpt2)DR@Mu0BI=F7$ho>_$)+v z0_kz2$B>GVh9eC_Dnfb`X(ZAJq(_hlM%SZ|Mr-b$#Pw51@w`&IQrU_QDEyGT=y_M9 zqezF5zC${QbO7mFr2RNHW_Im(j=tmNG~8gkMs=E6r^X7Xtwhley1W$Ln1w;q|hQd+G}k&@i3Ar$xz`( zxu##@>m>Uwcp2uOJb=k~jWdc}{uWMp}&Yn#@nvRNh-iuOlr-dJ}0G z(o&=)NN*rfxmr3#*F;|nTP5$Q3_adS=d`@j^=b_c)sN_DasPw+b@Dk~e~z>r>0_ji zkTxK#NBR(n%B4J?A#FsW=k#nF(pID`NS`1P4n5z5v{_ygOnLSxu89WSD~Wj1^8SkE zUdwNLJddvf-N)lA&$M~9*GgGh+6sbQ}ptXgi-wzobPe12Gtwe+PrMWWAn`!>s8T`>)x+4N%(fqn4Qgfu%NUh|33;fcYx*bv*B${VuA>EF2C(>O=9gyxuBL0Z4 z&Pewo(Y*Rzq%KJGoW4g+^A(!6bVs83TprRuB=YSk5Bc`w3J@a1IdMyjikuYiR&CBH`1j@l$ZJf{gT{*(yzYbL)_r6T$?@_nf>xxAk38+ zji;o?hPgC^ z0_R3Rx~`daL&}QLs{|w?HG@ZCqPF?+TUOtj_Si3FN_!mp0M(_0AdR-=ys&xswRM>r8Wf`kKGLb zd1vm%BOi@Ac_~AhWdQOIs1Z)Pwr6%gKP~ zSR<j+IoEalk1CGaKh!?d5wd-sF|2yeEB(6xX`V{N zZ0V;3__-38?v~;OV2#s)D?x2fcO(!n9 z8UM9q+z$8aWX$W+G^n&N6(cmg(hf0 zNZ_l8a4H_y1O zm_^i@5sJ+j2n_b1BR{+2)&w<;I3oFBclcn<4}TdoxYW-=I;2%g&_1M9Z!bDDtU~iQeqkKe(RTnLz4mRG zKk~NyUv~wBMA{rioBG%Op>!$8lOexJ$U_HDHC=L7 z>T|>a+739Rt>wX6&4Fj8jOtVE0m(sASak^*)TeRp)jKY&$6RwOTqhxyAFaLh{&MBt zWE?K2frQK)(de?n>wh~)IO(jT6dSt=<&5f5#^2_N>Ae}k98gr~YCx_Wn)~R^Z_hVm zts=2;oQ)s_?S68v^G;4xIiLt$ML=eL(D2=MMQ^qu2y_HE6zi)5Nax){|MUmfKzo&% zjsTi)b)Wp|WQE5{6K|pi`=mvB`RxrmS9~?;9@5_QR4Ba7Sg(!s z&A6Ys-S!+$C>*IcspzTk{Z8f)2lx-=7)#XS&vS=7k#MCM$Ew%(_RCz+=SV{+;;XNT z#vBZ}T=cAc<3FBqW%(=YswV2mK-gYmJt#?=8cWnfM-44szCY**!YWUE`1tg~o9#2D zrGQz^F2eW?DJ}Y>*@pYyJK`&iF_?|Nk#%WiHZB;qkFmxZYvSK9)?}48Ep7Ln>%lfD z)a#q3W}s1xrDHs5h8zXLy^DdY0XlZCn4*%Ris{c!8g^iro;CRw#*d zjJX~SmTAQJ7jq# zIp=i=fsr3AA@%RCJ$J)xd#f;ng)k8i8Zk~RZTnKy6+Lcd2!=YuGytLA*rDi-8Snq6 z!&MAPC$G<#S{g_ZwVBg5UpK_J@=Ae2?nYZYCf;gYf9}yKul=xzAv`$V1qijz6)#TN zTK9<~^CSeuD$fxP6!_6jw>@2UY^MX;0g>*LaXD{+W%4>}%o#p?!lDN=BsDa5Nw_wa z&POPx3d;HKyJIF#FLM}L1@Bgj&T!SM08Q0X9>*W=Nh7A8@SIb8i z2YXRJCC?u|vV{Dw_;7ds^66EnH@4(a#-Il`X=-~J?g{nn&}D;<9L<{iJ5kHz5%6oM zM;$<>?)=*KVD=dFdNdq+Pk#bJZCc?ud;Wmy3M;FStMK@GK>qP8s@>=N!_X=P=N3Rx z0GYjPYNal3FUeLR9RZ;^M(Nd!f6Z<`dZ!9ezN&sr$HNa@^-R6*&^|QBK>NhcnJSjb z0Tr728S{2Gl#N=u?vU5kOx!;8yjsoxKoGqs4o>Qv`QN*Ko2o)eQcg*zl?2DQrX}Ip z7{?g0rW&sn75|=pV&|%+)5enS2GUm13&xA&)IDbn>=Ir4eo?SHd zkP4}b$?46&aqek%zsr3Pny8d>FCaGnGGTez;rcDxvc?M?^+-tFjaOdxM(dI2^$L!$ z9y|pc{9pX@J>8xT{Fs5cnSzrzcUJV;SbG!a&I-^h{J%@TQhXQQ<0l5Z{jlaF{ciQkGc!RJZ?0OqpmvElP=W~Z_t}f&5solBg zS}upjtaz>wdxC{0&5@+c&>Wa=kbAd&C_2w}40iDuRKhUb3roUehav zobAHpv`mE;JsA)TLB$omd#*v9w4VDILP0hY8N`C&T2r&?}Z0B2@vXYGjfV{Os}vG(G|u)UswnTT%O|Z ze`z`9$%2!GDrBXE{B!2WxG}%pd6FR<7uzJuIoiS*zU7(IWmKHKfRM&7+|;@GEZg&M zNeDFlgoNBTV_d7OfoH|+1A2wE5ed0+u>a)`pB%GCrB*&gc=%Hq_ny#TMcbC8OSNl@ zPLF`EH{jtn1t z#(JIDrvDrIPjNjl1Iy?$@Rin&7a(c~t}!y0*_{c%p{V~DSGEp$0kk%S`g|oJy-U5g=dR!81OXuq05Ra&l-lB3 z#9Xt-juZ8U1u8xaYI4=;6d=TP=f)q-yLR(>c;1i!^QK!4TRsD>!9nv|MQ_X=Qm<{+ zl@%yTK&?RS0|?dh^IorQ@A%7{!yM=1D1ou9Dv8D$_fuovU$>XAH&~L}k1hlR4PHFHP4#W<8efb{?4*R0_+Qje*Sns&j# z``?@_vuD`sjRHa|p0@)6)h-_M)4h}aPPd(92mx$)fDmuXZf`blPSb;J8In#_odO6& zFqU^7IJ0u+X#&!OklvEz92jx5dztB9kv#y{g!7GrRBo8ppjNGvYgG>Z0fcYkw_))gYdQzNpZQ{473W?+(D9469Io7IWyb^8i*l$l3;~2<4#Q_; zKbr6RF_R(O3R5Ly!|#*dXxMoF6@nVc?Oh4U+dZt>ZO)o!RGi(ioGFilzw*CX18W6T zAMSJzG?mk^`-)SWuV051C~5_gL0v#dIxBt(T=VzwQJqzs4uCWSDJnh+zy;KMY(r%REijz|gUq5$F0|6o4dI3W9 z`RdhuyWc-}{yhPqnhpbmS}v{Y1ASgPm^zsutfMmlA?frzb*3=mkv-D|g!EtyAjI2& z(fg)rNTiD}rP_lXGXqN`s~LQp=Ggqx#ILobl)Q zCo^Szi0kfvkVLnXZ@j6t@4BICIm0Ej%-xUIUz(TInjySg{*t7YyKK$s;mzM@#1OVS zA4{C0MUSm~H*fqn0ikkE00Jc`{x0ps<)748Qko$dSV&L14kI+xbnc%Ui+kJ_&H)5L zeB!trAXK09v7LUrEVuVCfkW%tECmn1G2#uPc)*cWVH7B24F!#M$mpsl8-sq5@?{SfbQuEY(9k22+=B;3Pgzx!+LVn;B-E6axoxd- z4t_m-#i%7#cjDmZTL-^;-w2Goa@(fK^2w` zJco4KUx1J$fB&kPxAgqv5bS6R+7t$jyR@y)-oOLC`6GS3U@ghSlU+3CEs>g{y~cX| zILe_hYf`hHs+|s>9$&hYf)mWO({_SzgIUX7U3l!)LEsuOUFwa1c>IwTw#)Cn&6l%n z;1Y_uQ*E%%TXMZ&%$M7G|FRup<48F&?U0a;+pjO%clfT6JfCA*br6uNz`^0pr(XSH z_CcG3psJ?;sRT&qjxE>=*?%}Vps6!Bkb|bJ>DQC?pWSg+eL&)XZSn#y_McHup(`guj28^4^`k#|uc3YlRd9b-+@Z8?h(f;866esntG4FXAD6B|F6|(AYxG9LRW)ziHc5q4laN>6 zs=V?)pVh&fN#WpTK!}5>wufhos8H%d6_P1&Y77~E{b#$=TB#7@mNTyDl)FSZulDXb zw$kCnLsV*WBxLRSMSVV5zI(q4G3L#fgA<@e?bBs`o2q-S{{nG7rKaZrNdctc-4mx? ze{bf`YX2Ia!LX*?)Cb1hb*~qpEIU*p+2AasF`SYFh$2aRL}R9(-IxuQo%7^<2SBpA_uVk z|0Cm%b$yJvHg2Dbi&GNm7;{h(Y9-O$|EY3}TfsmR;oTZb$Cz44a4t}8?2}(yxg}b@ zCRiYBV(Qfx%isd*!#;UQVjHdp#Jr^6@Uf1`r#np(dy5cMFljksdtDNVR_-cB+VDen zrN7#tJzJ= z=Q@tLJMe4TTtpfv4xO5wk)g{Qw_Kv!6dRw2W4ssi|BfY3eA^4Yf7vE*e z#+9Aa9CDM<)^`A@1c?rMaNo}Cehq3$iDI1F2nfXjF0cLku(26uMgxMqUo_=6j)HCn zP8HyI?z?%((xbxy5(n+`ouoEt`rUb_YrfVP5II9W0Z2LE{Ks=>ex+}3Z39RX5%)K) zPvV<2^!^-})_J!N(Qsa^aJh#$ytx6}#;dC5x&QOyYbb}p1gKA*yD&h}=vfam%DMjc z?X@KY0fNDmNKH?}UuF-&S9uYiQi z-!iRe&Y&+m;JO9rL7GlEBk`gj=4UNTIOr|vv*w4CHsQMlmZzGM$~Upq00iECv2WGA z2aiT}Jp+i0`>z3nR$mTp88P)i$D4b(rrdJn`UtLIy{c!B8Oh ze&&GV*Nn>D00?+yxrOb4>_K?ndG(@6?SAZdNTv2WsF4->*uQt%t|2SuFbS&py~)^hJ2>`JLU`lr@FWJ|UDdXrQnM zS=W3!t<~Bxj{rxpE_ohj9$I0mZ{lzByPUpDLeL7HFj@$ZMU8sy?q2!WS1L|hG#g2@ z+>bqL-8OgppMXfNOJ$3Fkj|#>6t?=T=jDKK|4A#69#jLh3MgmkkdE6@AKtbU)TH(I zdHj%1I5K_T)OWf*c_fX>fg-}bx#a$;)nBxKBdh5_;()XYGVr+r1*negh5Jr7^vt^U z6ycB%(rw|O*Au~bw;?mN^si4ZB(0L=J zuj;nBVsz0{PXb5k4j`y$#P(~y2gaWZZ@v`}$=l^<9MY=B3)`(bGCG^u6tswg@ql16 zbMe-5r<%Rg@>ql*!ip^dqy`|fK5DS=*0Ea;E08qXIzVayVw-Sw{lYu`A{zo6V$Qe+ z8{3^dlDD;wPW$`d?Y$8xQC5nN08$I(4E*#TUrOEHq;FC>m*j{Ycym90FZlV@{)vE4 zP2pb~zX?zsI3$A+yDGkUZeHdMjKlNoL7s?*T6^q^yLw*wP$pz$`bVl;{q z&vixYVC7BYeEJAk7xmPJc5UGT;BQ*4u<v8k;X4L-UKY(BdAeB=a5TZ70&y5wgI_*V_gBOl*VhQ9)vOep7{_(2iUa1QRy}E^( z&Ig3J4v)R({M!rajbI$s#3g`K1Z3@yoA#V|v=i;KLodN=95x-@p;zzG3;zL5S>PO9 zG_Jy~U6%)eBQ@T*oHB#N*m&fDVVB(BV>(%XS&q(5zb1m~7K4SpO?+v2k8_WFMslP6 z0}tgsK*$s7u-@tX^rkcPrbaU%9lVQR&+$5PpWU1H-c`N!koA|APiGIHJO?9j4{_0U@nA@q6~Z0aeP- zt_dlfmVP0(PEIrBcBPrRp#K((xv2!Cnk2sF=!~` z{Mu#e)meKx(TEPOlRD5nL&S)2?I%;p?i;;qF(5PoLfAQuaKxS;@?_Q7K62DcU1&U& z8s8H*RZ*XB#xywJuFn`6Po=B}141?JQ*+}xh0V?j9Nz0uFU+SL7S~(WXKlCDN466V zc|sYsR)ACj&SU$JySwaYNnVY#z4uDU?1_IAZBAQvR)rY1!c5>)2DMdh|KP2#;ZGVD zB()`gQ~~6xwr%=U`?TGM3IvuM5UNkxasv(=*?k$UVl$4JB{%lPjCnKO7deL`z>zkwA0RZ&{Z#Rr7rrbkCf=kb?gE6Y zSn&A@N6ULhy#@$bOW1=0fKZ=ndUDR~WuHCP2@vY1;H@M+MI(42cC0ucb|#EKfh$9z6v~d;Yg5E42gGxzdv4H^lR`%)rSz>+uXC679R_sh{7~OIDYe_;0j6rM zd}qWH@Z(AAlt`Z2pTfr|;U>q!`wN^QPcV|@4EW%zd9kmcF}Cx^+y?wPo?N>#;2-Mt z6D5Z?ixC@V=lkVVI6vqP5f9OetUwUC-Cj3>8lj@bj-V(0Nq!`NZ45ZQB;SXe4x${k z`vWd_Tt=L06aeiI9L-!zF(clfJueao8s(z$JWk_`Vb@^0%o9g~AUfsK<4DNibTg2O z4!3IDIONWS7auB0^#^@}!l{8!Zew`>Q)9bwWru|z+ZxFo##{O5lp!=oVUgY8a)syw z2|JIXcpuT}!0|+R5ucan+u8jI2mCm^#P12`xm{sLj@zD{pOfPb5pWg>P{Rgt z#L-LkFb=uL2}mKdAxg93;0@N5Kq#z~?DB+zfiN7_fZs$gI=LYvou_ss|IFB57S zS%8Y>&Q1k9+J7e*O0yqlLn5o5|t z2gUHIOBVSxb_ZyhJC-N6Lj*x%8Ub=V?vP3U)Mf)I?JYR5?pMnBOTbkwnFlMoKqm$P zhkjI^P59&!t$@J4Yx8Bnd6t7!3r2?w!lj+yG<-0o z6`%5iB;d&_U|HJ`UgcmMoYe;jV%xe!{ZxH~OKFi;Ico|9D3{=hPH8lkp>|vYuJ$&m zT8R^kfGI9mepb9HBNGjVz|`I{elrW6HA6HNtPa?WZKX9DzW~^pJE||mEg&!tcp8fM zTEuOluEai$JOmMW1p;*9ArbJoT^J8Kcjc6qg&hN@rFkRVT zgf_NYh*85Mp{g9Cvr_EBTyc~`x`3)&f?IMC%<)vw0#m&~nRH+nA-8LrLeem-%uK|m%?7gCTNa@eQ+&1>5c$_mfdMOwA=K_e!gjG3NNk5;z3rF``TcJ8G7Y>~ zZ8l<6gT?C0?hM}Q_1ZDPfqk(HXEoOyp-st{BZVQf>_KiP-G_ttHIYkfMidwO00q*4 zUKTADQ#5GA+=B!S$*6x$zMm&T%r}L@*x{v%aFH)N;5Ac!agqmUiwm@w?1(0HizyGN zG51t6%Qn>TCW=-#<%Y1boP^C)l(>AI6lSzhff>C9rgf)_>IE>P*IXuhpCIOP!-QjU zf+a-E7>nTR+M|N;y8XG4JQI=XZXYj5i*Yx6VtM?cyx3bu<3{MTlE_~(EwlXG3a5{+@c?%P36=URMZ>pEZqD# zflz@X&d4=LRAkM;%!tg3 zBbVfE&u$WzT>~5BK=30J2Ui)(c7)wH2?lJXrL;j0z^{qg#gRe83$?_*s2VL{qGl8k zlu<=f)fah4Ms6Phh7dQFB1$T5szi*q>4CAh>1?e0kkcJ6WU%7E+|Q|r1Df^TIAQ2v zi~1!H)v%Vlu%ldqX!N>7lyLl(@cBbpAtldAf_8zUgl8qLRhff};k?8uwjj|6;|asSquQbbjv{lo2J1Q`BbrgTWtg|fht;CN zVpoAlX%?6yFmu}AE}8HWww5ucAgHpsx_dsE%Y#x^5P6ybnp|y z5QxP+L2VSLxdc=-Ln;q~TKe%Yt?7(i*ySNP$`|>TakbzKpyv-`E`|{+c~0Xo!C?F& z4PRUbERmXe)@mjYQ*Ttunx#H$>}JKJfT?*wH>BYZh}K2za&k`H`F!QtfE<+C;u|>L2P&KfG3&q%sp# zpwW+A>_boIP(Z%l<#Bq%Aq6I-YO{fi_LjKFwC)y~HlfNA5&S^=X=JeIQX1SK1P5ID zxo})G0^!mQ=43*5+6Izs3UqmO!KLSrngE}Ffo&ap)Rxa$N7$r&7hHC<7l1LGkl876gN$43nsn7l8o*MaGGwHH?{+qU9OHB=@8M zX~F>(wPLG*c^r-|4Q0Wv3A@7H#sP#bLvEMd=Lnh=p_851R*^CHqGL;8xU|LCF@^|R znw{_Qx*C%&uManmn#4bWzbmMF07;_Oxnqfe*75+Z&u!pMd- zX)-O3z|-EMt!T3?D(dzH26@D8Da;RY^1WWJ6Q)ZT7J~t-eGy*J4oAmhQI^OgOAZ~2E7Prr5MpXK#yL-F_og{ zMh-`cyr#?anp_~Gxq}|d*nu8}jYk~9M*-16D*>3~Fca%5M9Fc;Ot+v|NQp{u7;dOV zjf{aPi#}~i9W+iv@!>OvZwOvD%eKl@5Vm_AzHFBxiHrz#TI9kW<06Me2o?iSIE<~Y zA&1=+@<6XGXhQk@7@e$gjtp z;6BHS4BQQT=Fo|u3)4w+&U0bD@`hOFN{GwHS1=C&UW>A*2U=wDAV7(Y#}>H;``v|B zSprr%>J8;181D;shFR(XvN;`2?A5j?)Q8!3z-85oI9DJcm=Z5gUs2eyT{usM1y_FT z-G}2!v0uxaRvjmpYe6FD@y9Q@#8)ht7Un$mrWS>vMYj7Y#^(> zr5$r(I2=T8U}Sr8;o6&!(&Pdq%^kcO>AQi3pNA6jnVU8nNNI2FwClwxm@f{-rt0ts z*{FziF9_{^qU|}b`w=+$n5?)RK91>eTnGj=!gEeKsM@5u1V$0`1E#EZbE_o)OTB>u zDV>gjJV&G;7cV%PZAw%#fr@&=^Bs0w3f$Qqmsh&AVS1Iwqz;LhiE07`p7&VIrsyPY zVABuukT#}53r)(7iXKSOYt@}L!DNuO6+t248BZA4rRIjVTSQ&dOdzJ-fQt1@OPrhz zJaNI&vQp9H?Fr~6cJ`>XfvB-MWEuRmGAa(Tak?cjgba&_3Yj#EWtAKSv>=yQ89axH zpu5n-eH-J# zbSu6j;T7o=BLEkriP160KhrBjK&TYCfv)5-lfI&EDH!_fO*Qh0Qy76t@5h?HSQFqV%5?RZ?#g*t!yLO4YH!uW_iUW zTaDHloe-Fch5(yp78f+us~Qhl>W%1r7f364~=1KbICK8V8n~8Ly@dVvc zqEIUBU%Lq+#GzA=08knKdIVuoMnz~!kj8?uo5@eu1cfU9!Ytq=X>7&2ASH0e~0|p3x&RSNQ%qU57XBS@& zuxe`|ao`DTQh2E$k{kI3jl84z9vyH2R|lg4A2Sh!&`Aqnc}I-n1rcmajcA{UHxuO( zoI99{^;zW(LV+&(sP8)@l`75?5aZMieng97DNk+9yUTylb4#yWt3!ELwZ8hIRe4xlxYx z7E-5(p~X-l+FYY0prW~>rjy%$t(lQ(3<~lJot+L%PmTv2%Oxnq+cR{ZX4Ww#Q8E1& z4K1Z&K8({k8CZ)S!|5>%O8?pul&f_*MM#Kda58HZ^I8z5P7(B&By2s?vG8YUlsKkZ`hFPCt2svsd3r4_Vm%6dgjQUi6=i|>Gn1;+4hJy*5RUTi(ksRy23lseE_o7e%NS4b2{t@5vH;*^^* zbV^b{DCb`*mTLUtn?)H6E{6KXZ(Gr;RNT%MYOsGjl@)txbV?HdO&M|+7thVCvmAMO zvknMLLiEaz)geL_s!OXR*d?UHtb~ADgLok#FZ>cXRs-#P2#;9>VuzOmRFt3ajO4^R zZQ+%~l8#p>s)pDvWl%=}w5}$0^%1Q?obe}{O(ktsbLm@X*-#O&hY_6?UkI^sdtxi8 z$V$VLMuM8BIih($0^_OpoR5VSwm52; zg^+Lnvz^Zs7VQsRS59tbmf@jB4{GM~-g4d8T`smp$@7~9idjvPCn6Y3vP2$DqvN0h z2My5SUnY%09a+i7C8@V;soCC&H9d@%CWMsl7J-OziQQw?vqbC-z?pQw5*KQrCS^tW z0b2ALnv}%sC%!R3LFyrS5ZcgMYzwgt;B*E@2vIo`*7z`V5ar*b1Q6|&TCIvHj>yI6 zTntp*qo>x*gu$5Z;H8)g-lZXRmrbRG)6k38r&YR*>HPATd%VG?y~mzMvrsL6OaMjl zulo9qGZYA877}z6;9PBtWW04UFCPaYiKCvqVR3@A+*29!*+Z~5^sX?UpzXznOg!@K zFh0N3?spe}Q!`(VT)-e5|LyQ9xxtstQw%=VYNcU=sL-+Af+>ozTMp{-^H9K)S3LMw zd1CNJ`7=;JQ!Z5x$wUEgo_bV#z}4O+rLX*oHkMd`t6U~kuC@t@XFl>=v}jVDKDwC` z$kFShS|>)6fE{zMb{`X&sagqi^+wfT6I_jQ16gy&wwf&tPN2`mk@CFx2Cv!hF5UwA z`c^9M7*_91isAeyfPYz_JPep%@K?Nm!M{m0K^&jJjeDX}Q2>1Uy$KUB`7{I>FR9j5 zln}_uCGf1B-*~eHe6n6*Tcr8Wk41ez_E_e(>F_qTl~;^v$YKQlWM@=UkYVd+B&V&qcR1B=rzgT z+Tg-))7RI4D6iBog7{n-j}EL47#rxb8Gf9a8}j4b@?7*QpW`9MDA9#b^y8fpp! zzUD3|OQYc)*qS?3(7FMYkHt~Jc)Omy8<*5rB0nW3(CDLbs)`i_@S3@ONWMGYY&g~A z0ujyK1$$)_=;oR$U6IE@t=hmBqVJ_(iyk62cm-YyBgXbZe z<*H<2*x{D4il>-X5`<0Rs@~ExUir$7N!`UPiN1OR(&`Q9P~FXgxf)L5_==hZIwY04 zRal;o#orfT$J|bD2bnOYW&&;X28_wBm4$b_++K(SM-JqK@CG%%ZX|Njkt+6V1SfD3 zgV-BFE+#gX!V+PFYQE3(m6(LQ;2_}{4-ZzM7gLr%)fA2a+B%fNwL@0{G364R(N-L^ z9b7M-a^bT%CX&|V0u{|2OehU3=1P#z44|R530XoqT@f^b=*0Yjd|0C7d2;i-NMg-qwr+2D|Z6u~3RUw-+%V)$G`l`dEiGG8J!3TEmcuxK%lTntH>=*GkyY zdIB|it=fN+Qe)@3{EbFx6!{$vnoGf_m7hOSjv(aLGNFlJnK%cLGlHeq2k5HS)|1W> zr%y4GWaVX2o}?b*prQAeC2M8NG{-}LjOGs9Sa$*zfQTxE$dJa|qkNjNn|C9Dp?@lb zh}yxPqsi%uqvmf;%1q!wUO_*sJt5^ImQ;E9JxiV-SZSozt^-$l3(3hi1&lH+@`wWw zmEoNZ>NHU4OiOuMl-I#`n%h3e2?Yhx7#(c=f?^d55;x6(1LEXI8WowDSS*1~keh689{X?4rULFFU0lbN)eagSpL-9SNHXe1!b)?fqR4B>t%0+j#4`f9pzC{Hc!%tN=>B`eUua(t*)?wgAo5}S_lSI zZ_y5M-h8hk6bp@6&@s_b6D)9G0#)bmow|4F+?^L#&|f1Muz4T=3en!OePz4G zgn0C~IvXM`Cn&}}0a0s(P-_7K>J1FE+_m4XEm^4MDQ%jNA?LO+sbe$`s70@tnAIpM zKg>+k5T9sfThALxOv;OGDSR?aEFnwX05S6nm=s$3I$9l7O*3e#H>9o7kee{8B_vh^ TK}1{-0qKwPCzby{{OA7wt%tK$ delta 14274 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 { +const router = Router(); +router.use(getOTELMiddleware()); +router.get("/", (req, res) => { res.send("Hello World!"); }); -app.get("/metadata", (req, res) => { +router.get("/metadata/:id", (req, res) => { res.json({ gitSHA: config.gitSHA, }); }); +app.use("/", router); + export function start() { + initOtel(); app.listen(config.port, () => { console.log(`Server is running on port ${config.port}`); }); diff --git a/src/init/otel/index.ts b/src/init/otel/index.ts new file mode 100644 index 0000000..6c61ecb --- /dev/null +++ b/src/init/otel/index.ts @@ -0,0 +1,22 @@ +import { Resource } from "@opentelemetry/resources"; +import { + SEMRESATTRS_SERVICE_NAME, + SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, +} from "@opentelemetry/semantic-conventions"; +import { initLogging } from "./logs"; +import { initMetrics } from "./metrics"; +import { initTracing } from "./traces"; + +const resource = Resource.default().merge( + new Resource({ + // Replace with any string to identify this service in your system + [SEMRESATTRS_SERVICE_NAME]: process.env.SERVICE_NAME, + [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: process.env.DEPLOYMENT_ENVIRONMENT, + }), +); + +export function initOtel() { + initLogging(resource); + initMetrics(resource); + initTracing(resource); +} diff --git a/src/init/otel/logs.ts b/src/init/otel/logs.ts new file mode 100644 index 0000000..344014f --- /dev/null +++ b/src/init/otel/logs.ts @@ -0,0 +1,25 @@ +import * as logsAPI from "@opentelemetry/api-logs"; +import { Resource } from "@opentelemetry/resources"; +import { + LoggerProvider, + SimpleLogRecordProcessor, + BatchLogRecordProcessor, + ConsoleLogRecordExporter, +} from "@opentelemetry/sdk-logs"; +import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; + +export function initLogging(resource: Resource) { + const isProduction = process.env.NODE_ENV === "production"; + const loggerProvider = new LoggerProvider({ resource }); + const exporter = isProduction + ? new OTLPLogExporter({ + url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, + }) + : new ConsoleLogRecordExporter(); + + const processor = isProduction + ? new BatchLogRecordProcessor(exporter) + : new SimpleLogRecordProcessor(exporter); + + loggerProvider.addLogRecordProcessor(processor); +} diff --git a/src/init/otel/metrics.ts b/src/init/otel/metrics.ts new file mode 100644 index 0000000..e2ab6a1 --- /dev/null +++ b/src/init/otel/metrics.ts @@ -0,0 +1,28 @@ +import opentelemetry from "@opentelemetry/api"; +import { + ConsoleMetricExporter, + MeterProvider, + PeriodicExportingMetricReader, +} from "@opentelemetry/sdk-metrics"; +import { Resource } from "@opentelemetry/resources"; +import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http"; + +export function initMetrics(resource: Resource) { + const isProduction = process.env.NODE_ENV === "production"; + const exporter = isProduction + ? new OTLPMetricExporter({ + url: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, + }) + : new ConsoleMetricExporter(); + + const metricReader = new PeriodicExportingMetricReader({ + exporter: new ConsoleMetricExporter(), + }); + + const meterProvider = new MeterProvider({ + resource: resource, + readers: [metricReader], + }); + + opentelemetry.metrics.setGlobalMeterProvider(meterProvider); +} diff --git a/src/init/otel/traces.ts b/src/init/otel/traces.ts new file mode 100644 index 0000000..0e8c24e --- /dev/null +++ b/src/init/otel/traces.ts @@ -0,0 +1,25 @@ +import { Resource } from "@opentelemetry/resources"; +import { + SimpleSpanProcessor, + ConsoleSpanExporter, + BatchSpanProcessor, +} from "@opentelemetry/sdk-trace-base"; +import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node"; +import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; + +export function initTracing(resource: Resource) { + const traceProvider = new NodeTracerProvider({ resource }); + const isProduction = process.env.NODE_ENV === "production"; + const exporter = isProduction + ? new OTLPTraceExporter({ + url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, + }) + : new ConsoleSpanExporter(); + + const spanProcessor = isProduction + ? new BatchSpanProcessor(exporter) + : new SimpleSpanProcessor(exporter); + + traceProvider.addSpanProcessor(spanProcessor); + traceProvider.register(); +} diff --git a/src/middleware/otel.ts b/src/middleware/otel.ts new file mode 100644 index 0000000..c1ace1d --- /dev/null +++ b/src/middleware/otel.ts @@ -0,0 +1,42 @@ +import type { Handler } from "express"; +import type { Span } from "@opentelemetry/api"; +import opentelemetry, { SpanStatusCode } from "@opentelemetry/api"; + +export const getOTELMiddleware = (): Handler => { + return function (req, res, next) { + const start = process.hrtime(); + const tracer = opentelemetry.trace.getTracer("http-request"); + + /** + * start an active span with an empty name. this is because the matched route path is not available on the + * the request object (req.route.path) until the request is fully executed by the route handler. + * we update the span name in the res.on("finish") event listener below. + */ + tracer.startActiveSpan("", (span: Span) => { + /** + * TODO: check if bun runtime's issue with triggering req.on("end") has been resolved. + * this should ideally be req.on("end"). However, bun runtime has issues with triggering the end event. + * the res.on("finish") is being used as a workaround. + */ + res.on("finish", () => { + const [_, timeInNanoSeconds] = process.hrtime(start); + + const method = req.method; + const path = req.route?.path; + span.updateName(`${method} ${path}`); + + const code = res.statusCode; + if (code >= 500) { + span.setStatus({ code: SpanStatusCode.ERROR }); + } else { + span.setStatus({ code: SpanStatusCode.OK }); + } + + span.end(); + }); + + // call the next middleware + next(); + }); + }; +}; From 15a407d109533044b0460f33542d38e19b9e021a Mon Sep 17 00:00:00 2001 From: swazza Date: Mon, 10 Jun 2024 16:03:34 +0200 Subject: [PATCH 2/5] feat: added logging --- bun.lockb | Bin 146630 -> 156078 bytes package.json | 5 ++++- src/app.ts | 28 ++++++++++++++++------------ src/init/logger.ts | 8 ++++++++ src/init/otel/logs.ts | 15 ++++++++++++--- src/init/otel/metrics.ts | 4 +--- src/init/otel/traces.ts | 4 +--- src/middleware/otel.ts | 8 +++++++- 8 files changed, 49 insertions(+), 23 deletions(-) create mode 100644 src/init/logger.ts diff --git a/bun.lockb b/bun.lockb index 5c89bcfc3c2743e9cb8f163dbc15fc5df29f895b..3016a3566b0d5aaa497a8e6318217fd600824793 100755 GIT binary patch delta 33390 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

Y#0pi|9rt)6nVy`Kd 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 o;__a+Kgpn>NB{r; delta 27721 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 I^e;;MAJy^r`~Uy| diff --git a/package.json b/package.json index 699edc4..4343efc 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,14 @@ "@opentelemetry/auto-instrumentations-node": "^0.47.0", "@opentelemetry/exporter-logs-otlp-http": "^0.52.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.52.0", + "@opentelemetry/instrumentation-winston": "^0.38.0", "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-node": "^0.52.0", "@opentelemetry/sdk-trace-node": "^1.25.0", + "@opentelemetry/winston-transport": "^0.4.0", "env-var": "^7.5.0", - "express": "^4.19.2" + "express": "^4.19.2", + "winston": "^3.13.0" }, "scripts": { "prepare": "if test \"$NODE_ENV\" != \"production\" ; then husky ; fi" diff --git a/src/app.ts b/src/app.ts index 0b92444..6218851 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,27 +1,31 @@ import express, { Router } from "express"; import { config } from "./init/config"; import { initOtel } from "./init/otel"; +import { getLogger } from "./init/logger"; import { getOTELMiddleware } from "./middleware/otel"; export const app = express(); -const router = Router(); -router.use(getOTELMiddleware()); -router.get("/", (req, res) => { - res.send("Hello World!"); -}); +export function start() { + initOtel(); + const logger = getLogger(); + + const router = Router(); + router.use(getOTELMiddleware(logger)); + router.get("/", (req, res) => { + res.send("Hello World!"); + }); -router.get("/metadata/:id", (req, res) => { - res.json({ - gitSHA: config.gitSHA, + router.get("/metadata/:id", (req, res) => { + res.status(200).json({ + gitSHA: config.gitSHA, + }); }); -}); -app.use("/", router); + app.use("/", router); -export function start() { - initOtel(); app.listen(config.port, () => { + console.log(process.env.NODE_ENV); console.log(`Server is running on port ${config.port}`); }); } diff --git a/src/init/logger.ts b/src/init/logger.ts new file mode 100644 index 0000000..990bad3 --- /dev/null +++ b/src/init/logger.ts @@ -0,0 +1,8 @@ +import { createLogger, transports } from "winston"; +import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport"; + +export function getLogger() { + return createLogger({ + transports: [new OpenTelemetryTransportV3()], + }); +} diff --git a/src/init/otel/logs.ts b/src/init/otel/logs.ts index 344014f..3fb0156 100644 --- a/src/init/otel/logs.ts +++ b/src/init/otel/logs.ts @@ -7,14 +7,14 @@ import { ConsoleLogRecordExporter, } from "@opentelemetry/sdk-logs"; import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; +import { WinstonInstrumentation } from "@opentelemetry/instrumentation-winston"; +import { registerInstrumentations } from "@opentelemetry/instrumentation"; export function initLogging(resource: Resource) { const isProduction = process.env.NODE_ENV === "production"; const loggerProvider = new LoggerProvider({ resource }); const exporter = isProduction - ? new OTLPLogExporter({ - url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, - }) + ? new OTLPLogExporter() : new ConsoleLogRecordExporter(); const processor = isProduction @@ -22,4 +22,13 @@ export function initLogging(resource: Resource) { : new SimpleLogRecordProcessor(exporter); loggerProvider.addLogRecordProcessor(processor); + logsAPI.logs.setGlobalLoggerProvider(loggerProvider); + + registerInstrumentations({ + instrumentations: [ + new WinstonInstrumentation({ + // See below for Winston instrumentation options. + }), + ], + }); } diff --git a/src/init/otel/metrics.ts b/src/init/otel/metrics.ts index e2ab6a1..3995e6a 100644 --- a/src/init/otel/metrics.ts +++ b/src/init/otel/metrics.ts @@ -10,9 +10,7 @@ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http"; export function initMetrics(resource: Resource) { const isProduction = process.env.NODE_ENV === "production"; const exporter = isProduction - ? new OTLPMetricExporter({ - url: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, - }) + ? new OTLPMetricExporter() : new ConsoleMetricExporter(); const metricReader = new PeriodicExportingMetricReader({ diff --git a/src/init/otel/traces.ts b/src/init/otel/traces.ts index 0e8c24e..0a55a29 100644 --- a/src/init/otel/traces.ts +++ b/src/init/otel/traces.ts @@ -11,9 +11,7 @@ export function initTracing(resource: Resource) { const traceProvider = new NodeTracerProvider({ resource }); const isProduction = process.env.NODE_ENV === "production"; const exporter = isProduction - ? new OTLPTraceExporter({ - url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, - }) + ? new OTLPTraceExporter() : new ConsoleSpanExporter(); const spanProcessor = isProduction diff --git a/src/middleware/otel.ts b/src/middleware/otel.ts index c1ace1d..301100f 100644 --- a/src/middleware/otel.ts +++ b/src/middleware/otel.ts @@ -1,8 +1,9 @@ import type { Handler } from "express"; import type { Span } from "@opentelemetry/api"; import opentelemetry, { SpanStatusCode } from "@opentelemetry/api"; +import { Logger } from "winston"; -export const getOTELMiddleware = (): Handler => { +export const getOTELMiddleware = (logger: Logger): Handler => { return function (req, res, next) { const start = process.hrtime(); const tracer = opentelemetry.trace.getTracer("http-request"); @@ -32,6 +33,11 @@ export const getOTELMiddleware = (): Handler => { span.setStatus({ code: SpanStatusCode.OK }); } + logger.info("request", { + method, + path: req.path, + code, + }); span.end(); }); From 18eb9256c8c447834fcbd20d0f62be2ab0d4409a Mon Sep 17 00:00:00 2001 From: swazza Date: Tue, 11 Jun 2024 19:33:46 +0200 Subject: [PATCH 3/5] fix: introduced request metrics --- bun.lockb | Bin 156078 -> 156628 bytes bunfig.toml | 4 +-- package.json | 2 ++ src/app.ts | 5 +-- src/init/logger.ts | 2 +- src/init/metrics.ts | 16 ++++++++++ src/init/otel/index.ts | 27 +++++++++++++++- src/init/otel/logs.ts | 10 ------ src/middleware/otel/getReqAttributes.ts | 16 ++++++++++ src/middleware/{otel.ts => otel/index.ts} | 37 +++++++++++++++++----- 10 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 src/init/metrics.ts create mode 100644 src/middleware/otel/getReqAttributes.ts rename src/middleware/{otel.ts => otel/index.ts} (61%) diff --git a/bun.lockb b/bun.lockb index 3016a3566b0d5aaa497a8e6318217fd600824793..2d8e18cb2052c23f6e154966f59da16614525cfa 100755 GIT binary patch delta 24923 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 diff --git a/bunfig.toml b/bunfig.toml index 119113b..5c38fc6 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -1,4 +1,4 @@ [test] - # always enable coverage -coverage = true \ No newline at end of file +coverage = true + diff --git a/package.json b/package.json index 4343efc..cc7dc1d 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "@opentelemetry/auto-instrumentations-node": "^0.47.0", "@opentelemetry/exporter-logs-otlp-http": "^0.52.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.52.0", + "@opentelemetry/instrumentation-express": "^0.40.1", + "@opentelemetry/instrumentation-http": "^0.52.0", "@opentelemetry/instrumentation-winston": "^0.38.0", "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-node": "^0.52.0", diff --git a/src/app.ts b/src/app.ts index 6218851..4bf8f35 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,15 +3,17 @@ import { config } from "./init/config"; import { initOtel } from "./init/otel"; import { getLogger } from "./init/logger"; import { getOTELMiddleware } from "./middleware/otel"; +import { getReqMetrics } from "./init/metrics"; export const app = express(); export function start() { initOtel(); const logger = getLogger(); + const reqMetrics = getReqMetrics(); const router = Router(); - router.use(getOTELMiddleware(logger)); + router.use(getOTELMiddleware(logger, reqMetrics)); router.get("/", (req, res) => { res.send("Hello World!"); }); @@ -25,7 +27,6 @@ export function start() { app.use("/", router); app.listen(config.port, () => { - console.log(process.env.NODE_ENV); console.log(`Server is running on port ${config.port}`); }); } diff --git a/src/init/logger.ts b/src/init/logger.ts index 990bad3..da612e6 100644 --- a/src/init/logger.ts +++ b/src/init/logger.ts @@ -1,4 +1,4 @@ -import { createLogger, transports } from "winston"; +import { createLogger } from "winston"; import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport"; export function getLogger() { diff --git a/src/init/metrics.ts b/src/init/metrics.ts new file mode 100644 index 0000000..cce14b0 --- /dev/null +++ b/src/init/metrics.ts @@ -0,0 +1,16 @@ +import { metrics, type Counter, type UpDownCounter } from "@opentelemetry/api"; + +export interface RequestMetrics { + totalRequestsCounter: Counter; + totalErrorsCounter: Counter; + inProgressRequestsGauge: UpDownCounter; +} + +export function getReqMetrics(): RequestMetrics { + const meter = metrics.getMeter("request"); + return { + totalRequestsCounter: meter.createCounter("requests.total"), + totalErrorsCounter: meter.createCounter("requests.errors"), + inProgressRequestsGauge: meter.createUpDownCounter("requests.in_progress"), + }; +} diff --git a/src/init/otel/index.ts b/src/init/otel/index.ts index 6c61ecb..71a5a01 100644 --- a/src/init/otel/index.ts +++ b/src/init/otel/index.ts @@ -1,7 +1,20 @@ import { Resource } from "@opentelemetry/resources"; +import os from "os"; import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, + SEMRESATTRS_HOST_NAME, + SEMRESATTRS_HOST_ARCH, + SEMRESATTRS_SERVICE_VERSION, + SEMRESATTRS_PROCESS_PID, + SEMRESATTRS_PROCESS_EXECUTABLE_NAME, + SEMRESATTRS_PROCESS_EXECUTABLE_PATH, + SEMRESATTRS_PROCESS_COMMAND_ARGS, + SEMRESATTRS_PROCESS_RUNTIME_VERSION, + SEMRESATTRS_PROCESS_RUNTIME_NAME, + SEMRESATTRS_PROCESS_RUNTIME_DESCRIPTION, + SEMRESATTRS_PROCESS_COMMAND, + SEMRESATTRS_PROCESS_OWNER, } from "@opentelemetry/semantic-conventions"; import { initLogging } from "./logs"; import { initMetrics } from "./metrics"; @@ -9,9 +22,21 @@ import { initTracing } from "./traces"; const resource = Resource.default().merge( new Resource({ - // Replace with any string to identify this service in your system [SEMRESATTRS_SERVICE_NAME]: process.env.SERVICE_NAME, [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: process.env.DEPLOYMENT_ENVIRONMENT, + [SEMRESATTRS_HOST_NAME]: os.hostname(), + [SEMRESATTRS_HOST_ARCH]: os.arch(), + [SEMRESATTRS_SERVICE_VERSION]: process.env.GIT_SHA, + [SEMRESATTRS_PROCESS_PID]: process.pid, + [SEMRESATTRS_PROCESS_EXECUTABLE_NAME]: process.title, + [SEMRESATTRS_PROCESS_EXECUTABLE_PATH]: process.argv[0], + [SEMRESATTRS_PROCESS_COMMAND_ARGS]: process.argv.slice(1), + [SEMRESATTRS_PROCESS_RUNTIME_VERSION]: process.version, + [SEMRESATTRS_PROCESS_RUNTIME_NAME]: "bun", + [SEMRESATTRS_PROCESS_RUNTIME_DESCRIPTION]: + "Node.js compatible runtime for bun.", + [SEMRESATTRS_PROCESS_COMMAND]: process.argv.join(" "), + [SEMRESATTRS_PROCESS_OWNER]: os.userInfo().username, }), ); diff --git a/src/init/otel/logs.ts b/src/init/otel/logs.ts index 3fb0156..dd737f4 100644 --- a/src/init/otel/logs.ts +++ b/src/init/otel/logs.ts @@ -7,8 +7,6 @@ import { ConsoleLogRecordExporter, } from "@opentelemetry/sdk-logs"; import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; -import { WinstonInstrumentation } from "@opentelemetry/instrumentation-winston"; -import { registerInstrumentations } from "@opentelemetry/instrumentation"; export function initLogging(resource: Resource) { const isProduction = process.env.NODE_ENV === "production"; @@ -23,12 +21,4 @@ export function initLogging(resource: Resource) { loggerProvider.addLogRecordProcessor(processor); logsAPI.logs.setGlobalLoggerProvider(loggerProvider); - - registerInstrumentations({ - instrumentations: [ - new WinstonInstrumentation({ - // See below for Winston instrumentation options. - }), - ], - }); } diff --git a/src/middleware/otel/getReqAttributes.ts b/src/middleware/otel/getReqAttributes.ts new file mode 100644 index 0000000..ff30c87 --- /dev/null +++ b/src/middleware/otel/getReqAttributes.ts @@ -0,0 +1,16 @@ +import type { Request } from "express"; +import type { Attributes } from "@opentelemetry/api"; + +export function getReqAttributes(req: Request): Attributes { + return { + "http.method": req.method, + "http.scheme": req.protocol, + "http.host": req.get("host"), + "http.target": req.originalUrl, + "http.user_agent": req.get("user-agent"), + "http.flavor": req.httpVersion, + "http.status_code": req.statusCode, + "http.status_text": req.statusMessage, + "express.matched_route": `${req.method} ${req.route?.path}`, + }; +} diff --git a/src/middleware/otel.ts b/src/middleware/otel/index.ts similarity index 61% rename from src/middleware/otel.ts rename to src/middleware/otel/index.ts index 301100f..c98524f 100644 --- a/src/middleware/otel.ts +++ b/src/middleware/otel/index.ts @@ -2,11 +2,22 @@ import type { Handler } from "express"; import type { Span } from "@opentelemetry/api"; import opentelemetry, { SpanStatusCode } from "@opentelemetry/api"; import { Logger } from "winston"; +import { getReqAttributes } from "./getReqAttributes"; +import type { RequestMetrics } from "../../init/metrics"; -export const getOTELMiddleware = (logger: Logger): Handler => { +export const getOTELMiddleware = ( + logger: Logger, + reqMetrics: RequestMetrics, +): Handler => { return function (req, res, next) { const start = process.hrtime(); + const { + totalRequestsCounter, + totalErrorsCounter, + inProgressRequestsGauge, + } = reqMetrics; const tracer = opentelemetry.trace.getTracer("http-request"); + inProgressRequestsGauge.add(1); /** * start an active span with an empty name. this is because the matched route path is not available on the @@ -24,20 +35,30 @@ export const getOTELMiddleware = (logger: Logger): Handler => { const method = req.method; const path = req.route?.path; - span.updateName(`${method} ${path}`); - + const attributes = getReqAttributes(req); + const reqPath = `${method} ${path}`; + span.updateName(reqPath); + totalRequestsCounter.add(1, attributes); const code = res.statusCode; if (code >= 500) { span.setStatus({ code: SpanStatusCode.ERROR }); + totalErrorsCounter.add(1, attributes); + logger.error("request", { + method, + path: req.path, + code, + }); } else { span.setStatus({ code: SpanStatusCode.OK }); + logger.info("request", { + method, + path: req.path, + code, + }); } - logger.info("request", { - method, - path: req.path, - code, - }); + span.setAttributes(attributes); + inProgressRequestsGauge.add(-1); span.end(); }); From 4cb646b75b60012bb758bdd19af86a5f4f40b051 Mon Sep 17 00:00:00 2001 From: swazza Date: Tue, 11 Jun 2024 19:52:51 +0200 Subject: [PATCH 4/5] fix: added error logger --- src/app.ts | 8 +++++++- src/middleware/errorLogger.ts | 16 ++++++++++++++++ src/middleware/index.ts | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/middleware/errorLogger.ts create mode 100644 src/middleware/index.ts diff --git a/src/app.ts b/src/app.ts index 4bf8f35..0dceebf 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,7 +2,7 @@ import express, { Router } from "express"; import { config } from "./init/config"; import { initOtel } from "./init/otel"; import { getLogger } from "./init/logger"; -import { getOTELMiddleware } from "./middleware/otel"; +import { getOTELMiddleware, getRequestErrorHandler } from "./middleware"; import { getReqMetrics } from "./init/metrics"; export const app = express(); @@ -24,6 +24,12 @@ export function start() { }); }); + router.get("/error", (req, res, next) => { + next(new Error("This is a test error")); + }); + + router.use(getRequestErrorHandler(logger)); + app.use("/", router); app.listen(config.port, () => { diff --git a/src/middleware/errorLogger.ts b/src/middleware/errorLogger.ts new file mode 100644 index 0000000..71c2886 --- /dev/null +++ b/src/middleware/errorLogger.ts @@ -0,0 +1,16 @@ +import type { ErrorRequestHandler } from "express"; +import type { Logger } from "winston"; + +export function getRequestErrorHandler(logger: Logger): ErrorRequestHandler { + return function (err: Error, req, res, next) { + console.log("request error"); + logger.error("request error", { + method: req.method, + path: req.route?.path, + code: 500, + error: err.message, + stackTrace: err.stack, + }); + res.status(500).send("Something broke!"); + }; +} diff --git a/src/middleware/index.ts b/src/middleware/index.ts new file mode 100644 index 0000000..4220bbe --- /dev/null +++ b/src/middleware/index.ts @@ -0,0 +1,2 @@ +export { getRequestErrorHandler } from "./errorLogger"; +export { getOTELMiddleware } from "./otel"; From 4c92319ebc3da1a8b82e51e856d47e30ecc11603 Mon Sep 17 00:00:00 2001 From: swazza Date: Tue, 11 Jun 2024 21:00:06 +0200 Subject: [PATCH 5/5] fix: fixed issues --- src/init/otel/metrics.ts | 2 +- src/middleware/errorLogger.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/init/otel/metrics.ts b/src/init/otel/metrics.ts index 3995e6a..f7cd17b 100644 --- a/src/init/otel/metrics.ts +++ b/src/init/otel/metrics.ts @@ -14,7 +14,7 @@ export function initMetrics(resource: Resource) { : new ConsoleMetricExporter(); const metricReader = new PeriodicExportingMetricReader({ - exporter: new ConsoleMetricExporter(), + exporter, }); const meterProvider = new MeterProvider({ diff --git a/src/middleware/errorLogger.ts b/src/middleware/errorLogger.ts index 71c2886..e76c118 100644 --- a/src/middleware/errorLogger.ts +++ b/src/middleware/errorLogger.ts @@ -3,7 +3,6 @@ import type { Logger } from "winston"; export function getRequestErrorHandler(logger: Logger): ErrorRequestHandler { return function (err: Error, req, res, next) { - console.log("request error"); logger.error("request error", { method: req.method, path: req.route?.path,