From 82091b1320e7e66e0286f7fe490cda59ff9fdb7d Mon Sep 17 00:00:00 2001 From: Uladzislau Kiva Date: Mon, 20 Jan 2020 22:13:56 +0300 Subject: [PATCH] feat: add jaeger http trace format (#696) (#701) * feat: add jaeger http trace format (#696) * feat: add jaeger http trace format (#696) * feat: add jaeger http trace format (#696) * feat: add jaeger http trace format (#696) * feat: add jaeger http trace format (#696) * feat: add jaeger http trace format (#696) * fix: we should set sampled\unsampled via flag * fix: we should set sampled\unsampled via flag * fix: flags should be converted to hex, not decimal * feat: create new package for propagation jaeger * fix: remove unused dependencies, correct readme header, moved out jaeger from core index.ts * fix: added jaeger keyword * fix: remove comma * docs: replace NodeTracer with NodeTracerRegistry * fix: added missing jaeger keyword to exporter-jaeger * fix: remove test for browser * fix: remove yarn for browser * fix: use same naming style as other packages * feat: added index.ts and version.ts, revert test for browser * fix: tests added index-webpack.ts * test: add test with span generated by jaeger client * fix: apply review changes * fix: move out from sub dirs * docs: use common language for docs * fix: test script fix Co-authored-by: Uladzislau Kiva --- .npmignore | 4 + LICENSE | 201 +++++++++++++++++++++++++++++ README.md | 58 +++++++++ jaeger-tracing.png | Bin 0 -> 61523 bytes karma.conf.js | 24 ++++ package.json | 75 +++++++++++ src/JaegerHttpTraceFormat.ts | 98 ++++++++++++++ src/index.ts | 17 +++ src/version.ts | 18 +++ test/JaegerHttpTraceFormat.test.ts | 119 +++++++++++++++++ test/index-webpack.ts | 23 ++++ tsconfig-release.json | 7 + tsconfig.json | 11 ++ tslint.json | 4 + 14 files changed, 659 insertions(+) create mode 100644 .npmignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 jaeger-tracing.png create mode 100644 karma.conf.js create mode 100644 package.json create mode 100644 src/JaegerHttpTraceFormat.ts create mode 100644 src/index.ts create mode 100644 src/version.ts create mode 100644 test/JaegerHttpTraceFormat.test.ts create mode 100644 test/index-webpack.ts create mode 100644 tsconfig-release.json create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..9505ba9450f --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +/bin +/coverage +/doc +/test diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 00000000000..5c6771c3a35 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# OpenTelemetry Propagator Jaeger +[![Gitter chat][gitter-image]][gitter-url] +[![NPM Published Version][npm-img]][npm-url] +[![dependencies][dependencies-image]][dependencies-url] +[![devDependencies][devDependencies-image]][devDependencies-url] +[![Apache License][license-image]][license-image] + +OpenTelemetry Jaeger propagator provides HTTP header propagation for systems that are using Jaeger HTTP header format. + +Format: +{trace-id}:{span-id}:{parent-span-id}:{flags} + +* {trace-id} + * 64-bit or 128-bit random number in base16 format. + * Can be variable length, shorter values are 0-padded on the left. + * Value of 0 is invalid. + +* {span-id} + * 64-bit random number in base16 format. + +* {parent-span-id} + * Set to 0 because this field is deprecated. +* {flags} + * One byte bitmap, as two hex digits. + +Example of usage: +```javascript +const { NodeTracerRegistry } = require('@opentelemetry/node'); +const { JaegerHttpTraceFormat } = require('@opentelemetry/propagator-jaeger'); + +const registry = new NodeTracerRegistry({ + httpTextFormat: new JaegerHttpTraceFormat() +}); +``` + +## Trace on Jaeger UI + +![example image](jaeger-tracing.png) + +## Useful links +- For more information on OpenTelemetry, visit: +- For more about OpenTelemetry JavaScript: +- For help or feedback on this project, join us on [gitter][gitter-url] + +## License + +Apache 2.0 - See [LICENSE][license-url] for more information. + +[gitter-image]: https://badges.gitter.im/open-telemetry/opentelemetry-js.svg +[gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/master/LICENSE +[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat +[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-propagator-jaeger +[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-propagator-jaeger +[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-propagator-jaeger +[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-propagator-jaeger&type=dev +[npm-url]: https://www.npmjs.com/package/@opentelemetry/propagator-jaeger +[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fpropagator-jaeger.svg diff --git a/jaeger-tracing.png b/jaeger-tracing.png new file mode 100644 index 0000000000000000000000000000000000000000..0c8693a2b51f612c02414cfea96e472f5ec1eceb GIT binary patch literal 61523 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 ?Z9tFlXCaH+}q zu$AWNAG6(OL=Wge1Cz!2)SqL6f%^e9d4_l+4*TO40{3UN$J}f?1{^Y1&Um``>3#?- zKHG9TgXjlVnRV0BlwQ~0ykA-AZ6t|C6Lm>SFx9era!`MFwcWwmeoFLF#AbeiGO~5? z?j4w$HOHyEe;Il6eT@9tGIZLwJRLnvl)#jtS-RUAH@WBLT-?2@HTPqhQT*ArGl`8; zk?F{#ZPss8jr{`;&-${H*4(Yd4PBG#MSnk{fVrH|yqf<(mK3sZs{}u8J5nd$i^J^K z2(y4{Ujv($d9~wb(>RMk+Lo4f1Eq!0p;yW3$zj3+d zyc>Bo=cn?NzAkE}Wk`w`Dm`M*ZNgy$vXBK;!|CHCDqxcKbuQK~hQof&?3fh1tTXJs z10!na9?$nk3toS~o2GE2VeFzM!K4Zur00M5_{}{S-9lIlwCUrsvYt=szZ9?MFkKsr z07dugJXu}#i=!5{eB7q`ojCz?RSxTc?vJnXMj$46J$JoyD}NQ!jRc{3Jw$uNKuRpg zTC=@B>iA;b;K*nF+i^zycG$``GL@e%U1Qg%{^^s%~7pk2X;;_ovl?b^QpBG&FzEQlEf1k?h zbmyV)@XwWfb6OkcQuD&SeaFeVFR@>K%nrL6yy%WGVO3$}EGH(4BqDJ%crgqO4br(v z2vvrS(+(0bam#s#i@fzuNi03h&*vNP*j1c#2o27404lcS{K^RIv}INaAImWBB{zi+ zg5EAO0`QQl1lr}z1v`W4p9n9G)ZbXdlpcotkO@|IaO;!xtIyoc%?oaLJ4te%i{?g% zMP2}2mOLA#BdR6->~tHIRz358O#kV~^K!EtE7Z24J)Te_9{OH+9II$mldYPL>Tu5& zPTrj9nIZofIa7wq5dxiSJmIgZ(GmNcb*+rfY&&rN`;DH}Gp~MpL!^41Nvz`7q~{jy z@9+z+ZYv z(}8j|t>O9Vbhpu#8)^HvM3c(toAI|Vf6RKszZ%RkOH*YrRi+}R9rSoqkjElY>nb8@$f9YWOdZ7 zeo8&XE4aigHI6l{Uaqj^ptZrn#pc-)`?{=lmampa)4F_kPb|i6|#y9U}~O=6FQx#m8FkVPbj3=(M$7>jZZot+B14fD|E< zcZ^r!^VsIF2YTnPSRDKKeA^viKFgo6AYho=&G*ZFV{8h}ZbvEY08PD2E-Z+{xL#)PdSB?FaA32R2XiHl1vMlk4lA?pHohOCY zdEbgG^^6j+Mz*q`se4^P1i|9v!1s`MopNZCH`Clr>1d;^k^7R75{TQiS^HL=ane5J zuQ%Imf-|JExDq;mU5#NmZ8*9f9>ZxGMoP289Krz)>vNVW=WF2FAh{p-p#Lc;4eBJn z>@cDl{h=TuqhMo(Vzr*>6BkkfKDx-@>6#*JSe^0Rb;L8sg?}!PPAe)t`oiwK>1zwf zILBQySF`PZ5*_ywgr3Ew{D_Rxn5fgbzP4k+A7SV{^RjF3L&MLj1-CuDH*0mJ{B#pH zpwMN)jT$|Nh!x60{^4lpn_c`cqWW&6Gj*5q-iUc%{?1yrnbQJufk$;1ybF*UQf<{-+OjDla(!rJ&3&%;Q!O)n*x;2aFY55qW%ioi`{@DtBgMq3XQ~=K7J-seR+SXI{(=)6k=br_*Bzx zQ>xRR81WCkO!CqHW_15=vG>2_5I`fzzY_<|!r)hee?;fNzsB_A_hM3cA= z3}=aU2GYCNu6>RoQ4=~@a=(x1Uk%!C2+uBCUe^x&ka>K?CvQ8i`9?6bl|d^+RmIYN#_9mR$g602_rmEr{5K)DzB{F$ z_U)inr9blbJ?0&$823)}DToCwF>DsPoyJI%Ej)8CUtS-UA8FgqRCVZmV9S^9eUdl5 zK8*092ALGeaA|{FlKP3}MA6*px5UVp1*@~q7sc8LC_Xer(Uy^IJw0}tjQ)bbCSz7l zx&=KfUz{14SDZ!pQ6uFNjQD7~qIY4N8E?F@kU=8__N%97bNGWB#8k=aFG}N|U6TUjCdg5+m zL55+Cu6$556#A_xh?t)32Il=*ok6DeX)!!#l1(Ta6#^k`Vc`G3*XGTGG9GNyb59yw zT+PmIZ`^&7M0}deFKElBGd~t-Iq6;~{^lUBWMN%mcfY@+z2{@eOwU+$cHObiGv{+> zIGL5m6z6qew;t5xFLGN;pD#24%)o2+_0Bv^@2bwxt`RH8MJ(~V3q|fPO z|J$k~>J9kHSvYFW&T+7=I505MCXRxqt*KCY*zL-&{Ygw^*N!n%x@9$JeuSCotXX~Q zX&_XKeM1nXy&` z<&vxxzhnnB$&*AsM(lJh3VW)m-}03qMK3*6vZ%!a4Eh^%pB0T#Y*? zOb5!puSt&-uH^4X^8^uZdsk_NC*>bIZr#x?M}Y)*xr^b=M#Cfb=Vq;=JQe%b<5(k| zd3ZkQ-{y`ilH%HZ5R0Y%LlN}!(t2MdF$E953C>Jm;o6|YyqYQbT(f$ebF^En5SPO2 z^=UtqyzqfCt9Ok1nypGfXs}w9@`z!Cxxu}(y}>KvLX?nO?meua--&spR+-%l)WXzUq{_F0vJB`T7q&uU zI-{g}Gr|y`dtW(Krz<0bHFUn$hPXsGob7a!g;!9$~~&sw%+3|YRO% z9wF?02Z2v*=P?ZTNkgVa%FIpimjH0}wQJYpT3^4AzHuY^2|GSiRYhf{?DrQE@T|#e z67U<~<7z58!Ho3uW+MPr1D=qEi-!Ua+}0wc0iULK*8l!R2JR*%#NHi!#QtOpE-oHY z2|#7QfJ`@Ve?)-0?REa#-E98Vml3?ME+zo9gm-l0UV_XJ&9E8xysRs3SB0 z%^>Bo`;7zw6|^^9g~`1S7g%C5K%aVnR-3LyH#Pjo8267|=t?ocfQ!V;4YM z4}AM0>|f9MfBWZL*34!H^b6@mrhgD=Yi}+_YirNMCmYKnGf)6a<5S)A=?% zP(?+>On>Q1Htcm`vqxz~)yJJUEPP$V3gPu^#|Zg8O?ToM75L~urW}1D8em@ske`Cdn?OqzSm1WBD3>5wxBr@y1ORtZHLm)@Jh}JJ}0D8sUdPRL)&3`kCZ2NWb&mdlV%vVT!LW^*}cn5wSFH*JbE`Uw}tw*8LF6Q5sy zBN0C2!PLO<9rZwr-mibmc&6^GQMPh?8e)va-~Si2m>@--V05wRw_yIRSPuLbmBAwz z!E~AL;X|1iumk%SkuHB}2V^lN05lB7`x|i~A9t8wlDe;ffXo1H{rhWB%GCyM|2df7 z%Wb42|Ckt-YH;l8i%fqZ`3Dt!_`hLA7I5XqD;Immi%GV>0Kqi=$(0WZ)~fwxqXPcM z9Qg7OG*kPvKPp(uRUBD$^KXwgA^_{J>x(5&`!y~g%s77=ANwUhf(L+ICB=28anaMp ze`gA2ur#K)lvpx7r~?IdM1Pz1$18wmiU2p0;+h5kT83ez{0rSbSpzb2o%js$M_j=o z(h2?%B7DfB*|J>Brt`3ge*p2>Szx-P&M1A<4lSM!((mtdc+~WGzq-Nl?zev|-febs zP855iJtUa-Z=(Pur@*^c1Z+uK|F@}Mx@882oCt<@%l&WCc3G?w;FZC^nuzUTrT1S# zaK$GcoIha!2<)@;-}vegj~DKMz|^n&*L!Yl9Qy~p06XC3WW5I#kcmUX#0MFqM?{LF^6pXwJ$6WpPpU2jU&?ER z^b%LxymdAGHjX4vH4uo1ogL0!w^=?CBNCIk8tjZ+RnOnQul{x0fU(a_pHL!Fp!F_8 zj=p?_{&ibk3*-@mg8J4dc1^c@{b>KXO(pD-F@%j<4Cq08$Zs4xzQ1lmQ{ps3{)`Ne z_X{4e4dk!eIIjLRGQc%HBy{7Dzi#{gJ%KWOCL*SN?Xs!FfbHaD@~|N;F|o#N zcOmV3&0M?0FveS-oPiNnI1rfE`Jsx!h%KGf6ybE$4ap#Z=q&10kb&6_ueQ{IiG zr8_AulOt93v*6W@0N4k-7T7QINda3Vm@Sg2)5$|l!@8rSsj~%n#EF`RN9`H>p{Ay$ z_i>zewNXND>lU3h4!+~4APjbQN-{{%8C;`|#`gWW!F$!I+qsS8t7fKKk+dce*FV8_=Mt|A52O_h$e2I2{U) zc)qgnd_91WYK|6~s1K&mX7YX$p6QkJ6x;03GWPj1Zvp$v{_9=ADs=2zBj&Ub;kn&{ zCBfj};H!I(qL-tg3XKb!jhG88k^4)|N>KZSqG!`2#sHWDEK_d5IXLLWZmPjc9AmnD zus+s!v0q~0KT+$_-wJzhjJMH5BI-FEKouGqI_Ck=dE%?vRPC@>T3YJ7G43|kO4jHQ zx)sxFQ`ygS9zidN+_4(|5aJ8y?z))bg8D{5NjYUY`I?%kkn7fS zWn~g*7Ulx(Gc#lSD1>d`lAD*8@Ss%z80B$y_|r_fYae%dEH?2NJ#S6k8`ahxsa)ti%rbM)--%u+Xq*2orcHRVF|(hv!+<9(6d_=m*0JF2>@i#=3Fbx zOUaPxZ{2%6sTOnGWs7>jo794!ik;Gx6Bqx=h1w=O!H?rYTzn-#rKi3GMn_I@>*CLp z%zmP%wt>-6fRQn6Q0p9mkhN4>iuczeKk*t~b`(8sQ$NXEpu6j7N=HYR73#V6>ybcg&K+uj4sz8N zG(2eOw?AsCt+1p)E|<;N6QUzF8tQqlHiA8;$dO&q{QBN?1&Rc=&DPddM%cxnH|9`U z^rW{diEkbjnvOTO=REe=da*k#cgo)K_Qf_Mrgzg3=lMu@M8qWEiu1!MOpx~x9`+QE zd(k~#zI~G@xKke0zgklx!0&r|z-DS_h{cNR5G*ASl=ID&ySq0mknvGbWq`y0E-IiP zC_6hFE9J{nl^Gc`Sdj#Zavlp`Fry6g5Z^Lk2FX*#{F0KAqM|ouQjGAkirU&FmVwp% zJ-x10&~iV!i8`#sp0b4fuh;er`v|AiAt(`1Pue&3d!*JeTwcrajtU>mKqWGAuIG6J z5JSvr<3*w8>}qq!_403y$xISWSQ*a!9+6nbH{}@>+hE?!@JKJ7qT}b!i$=^LrY|== zQg-Y;?`~PQNK!(AY*Y`Pg0eEEUAv)gYD$@v)lm)}mR=IfOQe{GVj38|0E(++s90Y) zML$1SWz+r^9K|O*MeV%N2jYn+fTwy64v(%dj3prxFVrgWdi1fPMMrSVN zVF#~xd3vyJSu4#-tI@lJ%aKB3T#$;@NBJSST1gF2X_&J`N7CIPecO-*nSt@%(T!i9 z{FGiSX-J-u5J5ivDyK*oy~eYaiO?5dAuGO}5)0$y4V~7o&KEc}7(;&2$x|x~C!%la zjVy9iTsn$e5!7_x$xVM6lQ@pu7chIu7)j{ml--_b8u~4Z{-;S@PlNEd>b5RJNgOU+ zfgr6O4faqWJXMR)dr9)S`ZGKPMQ`*R^Tb3)W_VKSdZ{mIu1`)HT3#DaA^;u9Q<#gx z<*g)Ck;M~5iNV;!;L0gf(s#6+qT;0#Xs<iI1JMJ#l=X|(fU%Q!VMOjX z^biQ(gjEheE0>|i zKp`R?U4Z=~&%F;Hb5eq#`NhTB*YlQ*O%VeWoEzAHyw8Wd6&=ndHc`^jlQSi7IDB=e z_`27L{~z4~cD8${kjA!r*3R;}63_VuD~$`PDY+w|y7|=9nhRW>(}a~a6Em3~!){wt z!ycZ!zT6wv0GK$D_v3PJhO+dmO2_S~(~Md2lLtI@w_#b~9GHi^Im4?Kt>LtM-%4}< z7>%4<=!yNx$_kRjvgGL~8|?aaGsQbdK^$u#0-q;rPkewYIVYPdoR_zrG79OEgWcqE zQ^Xp4tE4Nwa1~!)@_E9=WlJ1K8&>|IuFmtb1wD@)2B-k>m;l2h1(rnFyhPe-9G`$- zvfO}<2Qi}Z)=^a|<#>D=wfXxRv^ILys?|&K=~34N;?nzxEb*}FV;hJ!v*uIFRg0=8 zg=i9aYZ(2zXN{MFkr!ayWli(PzE0Pt<#a?d3k4tvlq3xf-klNS5y+za20 zN;~t9AQcc0u(YzWm>a9K4YRoPCGs;RW}3u1T~5-ReJ@M_69cbKk5LU{dm$@JP~UU0 zN>A^rf_uY&m_XC{5zW zZoU0{yPqrl;Mz2ms90wZp`cD=NTlVk%_uNNeLvnX%i;e*Q1i&qMj|_y7e23U}CUPyF|AvaI()% zfH|jfFA;?Z=!oO$C$)d@#TK=j#Lv&~d6nD?0h-ljc#2^>(3y(FcfEMKwKRzhacDGp zi*N^C{3WhoFFR8@vhN4j-Y=|94D+f-8oX+eEaj3&nMKS_eMxLA<;m)%H^zgECN?&< zU3AMYGCDdMoTL_n__VgRhJ`gYCg;#&9{n=#tS{bJzTbaO;32HDwe@JE8^v<<+8!Ep zHp|#g_$Y)o^60qRTgf}d7UF69oswmE=&$;SqA@cy4H-!ZmJ_t>NqKBnsU4E~c?Vje6%Y_G z!*l*NTaJ&RM9b zxw$v^3)SrPlkW$cQ&vt9 zYG^Lsm0m{@LFDf|gk`}$(r!_@dNs2?7hRt)2;t;xPEFlj)qamj#iIeNuRAV6T4tM1AK}QM;Dq zY{RATI@(BIpSH{HaAU&pK-Sd?m_!uc{FG}h+ewOM z?RColfm>9z%QyT`4$H1d#6MdGJ&yPTDefVWH(hVYojweg6=YlA1s31lH?D8jFS~g7 z{tWis@?%7JzvV>LEwcwIjku0Qaq;|c(aCeTOM(kA`KKCOq`-s|SoMbeX zIC3X5K>2a?UXnYa$g~#Y$n`Le3gP0vD4)3BEp>HVb%CAXZD6dWkP?<%VWA&{A($ zdMpBhPjLkhea&GH=Q>6I`zU(*3AN48Q~(WvbX)5S}04UKIq^ z*Yw_U|0xgOZx(RUxLPOgyZUvCIP^ zwklRus~{L;Rq$r>>A{y7l{|t>8uzW=;)!Uw8<#BI0bSEC#{612p;!G}Y57Nwm+z<$ z%EyEyYQHnMIw|QU0`yBcz?NDuP|8cmZkOQ#owHNk7_a@bH(1>b&H1x?(bDv72Odm4 zcX;(`DLB*+*aui0sNt=T)B6Y~MZT5qj6ERYQeL~d-&5(Amn&Y*9OS^hy)nN(@H)g> zgsf@Gbrx#b)ocaX$~@=KX)+K%Y1uxKZ0x`s0nZxrwE~7%-I{J%nUKIwdDeTMvi=G1 zi52GwimA8^$ALvQ*ai4h;2`}P=he>@6}?Bk6T}d)9xW3A39WA2yaKgstxa2{nBd8) zLkOmUOZlFza6$s$UyZd5oqBDu6bZN8(E>c?q59^n@{0{KKGDdcsVV|MD9Ka(g zK{S3aJY9WN4PHGiR3TX_SwfxS>KFzn2H4v&h5EX> zvZ8s2<96+4O`N+(q>iECMK+gL2#cz>V+b&NNDe9+_KSf5-B+3?VmnegZ+o#|D+!@8 z6W1eQ6d5nK=nsU_=C!3e;0Uzm?t5>yP+5?nKZtVQB(fSRLgUs0qjb-@VSh*;SW|n{ zgT%?VVP}99Yg{%BX%aq90pr)SqS7Ir0>BsKhi4L4tcI>;VI9)`mxR}@E%gK)kh?2J z^)umz=Y#mf&vKh`5y#8Bzz{Y(ePZDsNLr0G+)#z*xgjI3p3nhdz{sAi(|y3rL7ZTn zFh?IH(O-^PB0+2KbF$fF8?vK0+ zJ8R)Wy^D*JZnEapx3T$fkmdt<{>kD2_xbyavBvW;#)~aH0on94Re`(dq5Myt$cT3B zSt(fu%Snp=?FIS_j?%v)+%q|A>k(bJLcJjL|hrJ~3P=n}j98ypC#oTB>k=U9~H z@{RbFTPZhGvSYn}yJfgjNyMBuD79u0cb1@V)@C2d)J0R4XLiu~RHc06=u)sG#WDqe?ZgKsbsBnVZFE9{n zqDQ+x8`yfEj_nBNnwj_yy`hN!QK%w=+Sr0m&h>DPRuV5$(eq_xa0J6~<55^+dhF{_ zD!nDXhHdBx16)xe7thh|ZE9+2Vq#)!>{C2aON7*IQ2ANTRlnCa+;G5i@(e@+uG32g zs5}4|b82_?chfM*B5|(7JoH07-WDMlidf2vmq-m?U8j<$Z3X_wk+%1~HjH9jw!Faj z34oR0cJw?uSuII>Vt-o$nn=b&g9Y7{m6bI#oP1d(ZwVb#3K)>=LpP0=c2FOuWD}8) zV1~WVsVXEilpnCMHGh$jXgWA>)-$qP&9BJHnJP)*gRrw%OXb`tKAiN}#Cqx|a2B>2 zwBCO9Dm3eAxa9{%dvXcUueWJ#X%+4dY9q37xCI6To!8>@f5>W4e4*Y;<G`iIoLd{%w7uJZA25W}1>|v>h5kp1C|D5WeT*6YByRp|My8n_ zONzn<|)QAWBF{wu#Vld6n70)Zspg`aSN;sv15Ct(mt z{{JzB|CKBkyln{KYzqf9B|$&~1LUDm12_SW8Ve+`35 zPdrH3f5@`wgE+yMaFER_LMj^0NT2SyDzuUI@mD{$V+BJ)l(@ZVko<+wtGtQ@6IEYM zgtm%?ngw-obkjtAmpSsIlD3MD+T^&rUywqJ=hb63*n>wU-vY(?YvvwmeP(lrC((9L zwcm~Rysq{m;Y=>b6Z%m4e#?z^tGTNL-IPm9^c z3}<}8i9bnQo0*w~g{4@p5^g)6pUCro-{Oa=mzf;*ITJs#bx?yrmbDP9Gyn0LlDw>$ zdY0)RbW5}+tw`FXNM1I>H$SP2sAkXKNm}}Ltq}i_z{5Lc1`7fWcMtA!_MZ9s))aM> z+rXR`dewFB6IibCsYo*>c7=2;5@3P>x>6-E0TAG`Q z+8)d-F|PET_s6Nx;5YpxQ(k!XTb6oJu|}`M$!%sr#laq>s<=v;)U&?wkaAU$_{oT7 zD#FCE3Yjb$*QAjlXrX>bC@%lw7g6@;tKl)9dsZJ3f`X&Ap08b2&Oe4b`O2X*pxEf& zmz>ZI0s5hd%`|>kmgut@N7;tnbl*MJKGIjE7p?<*yWD;kmyOfGNs9}tQS(Jgy#5js~cER)NFim0=0}>DDcmk@SMQgvj_>fpCG;3IV68Fk*m`eGI{v-MCJ|0gG% znD?jE58)oM-13fCVPENzi5kvPk z$8-Cxzjyvf1P*Nuxy@oS9_M_QwWuE&+@m7fg&(%*8YFcf;V`n}V`DG}FjJje)#4K3 ztKbgvAAKjyX;Q18%OSD}PVMY&4Q($(D^U)wKeJBb7hCPvl<5RXZr%_9b)-Rw$pL<{ z2|KlRE(q=cT~p-E-ubr4)Ij?A8b4;eCT?+}CTRijHq4UURq(P&U; z_wTD&|2XmZduJT$>3pCOIXok!mhOfRuHJqANqv$~qIUsefqPfd?ryY2T`HSK8Y&G% z?3HWE5`ZHY%~979-(Ug)0uxP&1-Sx(0yor?Vjg(nTK!~e?CGgXaxye|uk&{CbZ_qY z>X#@bZjau9AluSQl^s4u9m+kDujm9ub zqm<#4gY%2?%c1T1L8(FsFNNK<6oTl)r}H`=eEzsT%TP|;eS>O_v9q6I_}A6?5hHLQ za8Y%s?}8i`drQp@apLn=AJI$cL{JjSf#5aUR*hHRzm>HZz7R);X+q22I3zi!MZH&hT;_P9?zC{Ao)9wG(=(>}?w)Sul%Gm;&)dUx zaPH&U`xlL=fi_&cfiyIMdU(9u&%2+g?#jqQp(E{C^qMxG^1cp>CrEs_+%)M9Z+aW` z7-vYpYjUHrTr-3~c6G-UDbSG`Xjqrags8GC-8V5bjq_}(4iZpcb#j}hZy#WifL9jx zwFZ^y-qX!0tdj ziNU;Ek;7d+Bs(biA(R@6_;0*o9u#-MEhzZbG*}SNM(wx2A*JPH!uhGI0g5Cpz&)$tlv^GN&Qw z&{AO(T`lAm18QL5W*K$+x6OZ!CD$a?10a}lS9p_3luKHBdtV`~e>S@C)vqyrz-dEK z$1NGdX>*@bOb%ftm_Zk!ab}U>wYjj9(tCn-3Eux1NaWu+LKHIsKo~$CBDe9M?muaeStF6cY%Ta zE-<3Fn;`NqpMI;xDLT3}gOSxu&mG@P z3xbf<(qFSWMNTL5_uIP|bdm9OnKnMkG!i{MZ-s@c?=`mm`ugF-i+g#+^4<`|_R-Il zK)xp?FBczDF1Ocy1JLVv`KkiHd$hjdPIF-$c$O+O zpc3u!e&X=qEf`%{^c=-50jr0Vof|hhowZ;fwoLe67kQc~CEDmNmO)Xe#Gpc-rBRlUlz6r_=DdlB}FUmz6ns+RT7HP25QY%CZ(7p-N*f=_6dY=6G+pVOGz14_Q~I;yzWiC8)t#l> zT+8yFtePD5wT52$xGWa970R9btH_It-RE0vBYjI@GI9zs3TA#?uaqWE6nG1Ej{U%C zwWY~Fwba!#(mT3`DQ?8^btJzPv9on>vj;$+1dNlbDNa2-J*w*Y%Lz-C99(So?y+;) z=CQMJSC^xwqtIFho>AYv%{=akP5gG2v4EbWF|g0xzN?4^XHg@Nt!8?<3NLO@ro2q2 z!t&P{hBKjDk^0|e0QdC~n%(7W--=INM@f|&eEss+7>$cIBd$ooMkpxLGtEJIjwWn6|XEAWEcZYO|6r~ zie^h0EMqD;K0elbgMpg*<4V7_g{|k!?(cLtJMWDbvX-9Q1m&(eO27@w{0`84hAaM) zfs+$81EMhVK*XCpVd{n!Nj3Z(q!+&Q7e#KXTAzZLyGMLb4yAREjY!ulZWGAn>&8hS ze$ftn$xhxIm}{1FuC{ZZWR?tcc*M^;mpE~_$*|(oHQ-Hli~G-OL4O&|-~1GtgbjSc>oRMgn3oNvJ z=LdnrlKx-S;c--oa4<2K7&L^5c^Jw`Bu;WnS;UvUzupJk>EfoLzIl_N)Sb*OveQY< z=iYx@rMwC|w2_N};ZVMAC-Pmq3oEnPP#a$v_bV6&ljbnT)TQR@|FZzlpffPg=8&$Y zjpG?OUiPxc?o42y!8{(x!u(@5w`1Iv7<$g*pf>bSVw4naji-UQs%*BYof@&rA#WP` ztAK&Hk*u2o7nFgUVwZYuvA3wX?i%gY`K9+wIwJ+0MwhA-0$RFW>~y`-OYi2f5Y+vR ziNRedZWHZiLo_M;7dLGipxHFqT5xM=YY!*Y{zz^2?NpDO*ytlxQ6SO-FmNlQPdm=- zAAK7*XYq@3drNX9_y(3q!$yICCGCPzmnC#IUW%18C4FbOm9bi3bYwbq<5gn%PZ@+INmMoi1I7Jd2Bpn`e9byIBkZ!OZjGA$Ed>U(--Cp z!Z9)L)4!u|oc7Qo$-mx2v{UyA+%=;a-4|4g(l*sSzJ<1tHh%p!+0+c@wGyWYHA47= z&6!q5=}EeLjCx?WWNkZ1YV@b!tipKUwl%U^NBK;KbCqL z6!tyAkzawiCW@D*)*;~}@uXF0ZLgTRYpPPX zOA^}~>)DsTUG=Ti59icz8Od%LKluPcCqSR|6>%BV+?_)9h@C`vW}?q2qWxYk4He7F zOeSlaS0pN%7R#$OalqXrUbWYB>E?E>3We6&+{N5 zM@FgRaeT2Ed~#vVKg$s(vT6_PzafRl@9PumaS>)8hP}knV&miAyOsZZS)VnP z(*CUq6jze2e#ueH?oNLkKQ`8N)xiCgu_Z(Ic9Cu&xmGr#>FlaY)<-FDX0-KWJ)3W_ zUqy&^Xpi6I`%C6y=Fgv4;W-ipiY0VM!0m9Tmg=gJ2nvKl zV`6c`kF;a}9&Z{Udiq<_*RE2Ju8p1x%D>zM|NCIGnB&SD9yrgj3{!7#6PVxLsakIm}xdq33dH1onhK1|hvR_a&Oj%VC9@W4Idx_1V4-|A)HwjEZXOwndGg zLV*&cNRl%M6gdfqAUWqCxyU&u6(|%W=WLO4&QU3Hh9V;%LV;u?XYRu7x6i)kocG#0 z_qDIJ_xQDJfU8(@&ap=CqmMqYK?9A^IHL!>V{=6YDPXW=X2~}H+z3A^^<33cY!oZ* zb_I}Owg-GV9}Qa$aA~;x1k9LEWcZ78r>t8a%Bn*)TpS{J185c2ft1t@T0+7-d@hT? zt)&oRXQ-4v-z%vMZoe*!ht zX+`~)`;7D@)}%&=&W2Akf%hSwe}2l3FaDNQoR#-=LQz?BzIR32m_3zQ@HNwqZLZWs zDuTGDjh#?^34SUah)=t}hdKXL6A9NTb4&_JRZOK!IYzKNSm5Lso*I5uf0AJ#gb~7I zCBM$NrZdWV_7|2MO1;67et)oJ`40ST=^Y(FY4p`S%18H1&^yPQYg)Uao>|4!KpCYmm<4be!7ilVNFwC~aFLLMA% zxJd^3dS)WiY8V+rf$0+c3Zl`!TFZtNql;Wtvv-GweJm>-154iwXrTzfNp6*cA+k6a zd*113B-W(vqif+SJu7*7JOuuGQwq-E2MB_%IZ~#PbPqK_To6pY(=Yk`Y7kFpZ8P<> zE9J(Drm&v6w9cb{&Xc3H8`8c4fim}(ZYG)Z%zb?i7RE_VE@_G#uo+cK zG*Co{OC>p6^{5{!-esBYxGQ=_rYRx~R)&I&Zd<@3QQ^_X#VG!yMpxnaA=EZ+rpiPW zHJbtqww@6rOJmL~h_=Z^P&Qq0N|r^m?_xvWD}rS2*^~Y?E=U9+C>ut-vY`BI3BN5(nWI>}oqD_fey$@>)ye)DDI90B~V1Q>B> ztPZ>$2jhVE^gU((qa?@8N#yWWN2ekHGdI4h5N#$hS&}j2!zpwUt1^382)Iu8=~cSb z-c0ELgAW}Y&Aw>o4$Y<})-bYDSm=>q%b8CjheM7b%NQDRHEGKZ3GrTe2xsMZjd{2% zDJ8^tmeE5}IIXUHFgR;6&sYfD6X6MG4*_AXq{nVYOQq2 z{K{Cf-J_zqGD;%I=)pkrs%=fw1(*-VM>bKQLQ!5zrZzjkhrXQK9~5ZLJU|neLs6TZ z5Xv#8m;g|@ZTN&MmYM_H$$WyMdOsxCaKm#5SJ!2@0)gH zN1{1@gQlbuFfzelT3c>Ldh8Hh-xJ|5tf`ns?P{y@aA=(*w!u-x$>C#%T%pRSH6P-f zxFIRT9s8y2=$6^WMr!!6(@2Ktoimf7dd-KRSP5hoBfX$;h07rp7Iz4|2t9L;{S=QG zqN<@50Bhzt1&Fs$|6zMqAY1?40-#aKtJ*#a%mwFM|AIlyZTJ|xBbtV%E? z4NOczqEaC~bV=)*i6{dm*DmvJ#Pb4~li6+ebubAo#r&uHfA!uMP&zI=X?~fGA z$jQzZ8Dk$lU(PC)mc|5psg_y!RP46Ne%65ZMyC$zleN&r9rA7W%&wlr@avs19zDa0 zYY2PsTq+U24-rewnyg8vSU<6wKtTM2DMf`TFKZDXX%ZqeybODz7XFA6ZbP(U8u}S1)q;%su$hwS*kvmdK zwnyCnZXtEK>plATJq)IhhRH>UA|w)+LbWK+>gj_GLzMP1-v0QCd~!Nx#?wEhW3cPPN{ z19J*UOfXjC>GtbNmt^0`cU88}gI%%3%a^~x+S;UpG`oB5xOP;U0mrU`Qr(yM6*t2F z3QkenBz65wJTCf_-kWAf3j)rCB+NT-(QL4 z-3E%FAt1$4Ic`U;t@Vg1d0kXKkKus@p#MNemVyVqeWhNFHHXtX}^nUezMxn!8ad^%vCC$B#~{Mch%rZt$aliu@6P1x@_ zKw!qHOt7&+{(qp^1A>HWkven2GRq_BFYKTet7iXBzdkNtOf!IdJcz9IKDLn%2 zuY9SZ{kgB6;k7&#&fzEDTFsD{#6xZd4<=YjyEEtM9Vl9vzE(0cH2jrlCL?Dk>{IuL z+bQ;65v2z-Q73|sJ34(yIKmI$i`L)2n@#(`dx|bu{w(Q3m9$n@y{q?UyMj%o924pq z#K}dMhGYRhs#H}q^)_R>OzkX+ic9iJig{`UB+wDLOUEp7)~vuJ4_Cr;dTS`J(CMyv zba|49Q2qUQy1O@VQ_mR&qnc3h^v66)+hy{s#zsqff&r4q;E~+S37#RwMl;g8SgHB& zPR8?+=CI=XZM}lC7~Bs+(O4?kfposnVM%vJT-b}|!Pk$S(Pp}FDFc8#=vJ4j@Q-|M zcqixL{(VhDdtK1utqjip6OQNsyLm-z{hkRs17p6mn(h0iXkv(LV7te@9rU|-B>x-A z!}&;2Wb*r^J_4MR52w(arcrJer#Om>y8;(l`xC`R*fMFAw=Y*rJj~mkO(9?6L@8IY z4oulF*S(YB14@k_KW;iM9yG~(!2D|EXx+_Kc-gzPPMWk7_O$gMnEhWZjY?h$2>gN2 z7_?`e|6XDRpodU_|3-R98WZ$H;LaD0wtrt6CMy~+M`_lfx*M1l@&xfexlf%Yz>+_- zkd#~&xb*@2uR^iX>X&zS_wfJGkBR_7#p7qvf3L)i!k`@euU1lB=KY7DIon$Aj?-qn zH=F}L6`2*C6b3_mpOxGy)LO9emt=jjE2`kI-WB#!@SC;2EA*r^5O03pg;7l%pO%b* z`ET1vKM*;c4LVRwmtwaFg5<0oO+7t5;AQ5;h$eTSGyPdRb8SK#oBZA_87z^mTO+Kt z;)sU+fS5T+rwJN5pio^b-B&>T^tnlPHZ`&l7!J?WH-aW_BQGs2LgOe>>v3Gr?$WSL zr_ivkj5~T5UvZN4v4>%e->GaYGziupBgr{gES*7S@P)X z)1Dg{z`&U+!uAZW7gOwKLH=44+p2jD99~#Ix{SkiI}QiS_SuBDvvI>EkXr$k8uv{T zF}S3~YfXr=fgm2x+%oe`RLO!Y`_Mr(fgQRk!m&vw?lhh^C{ z@{=moAYp_T&GtJ$o#LF{PI7`2RsKDD?)5{9vq-wTl1QBBQAhd2;;o5>%tVHd42^8%;hka# zfJRB)$22m8^I0f9{fT=^XfV_El{HDM|)9lF+JdvLI2CWEHkt6K?ofi~QijdfTc zp7bb(L=T`Bx#-8hml2nTg^n>iX9FL55S$Hb56ZCd(mgCob2a4hwJRC(YUF)}>h) zPm7K!ljr&3MQ$7CLql{VsukA_jApb%YwnaLzRoL|==}i@acwwMm#q#FXbHjcN!GC) zfn0hDv_aM=iTnI|zP_U6MN@%zEC-;pmqdm z&|*s;>It}qb;ki>tI$N|wx%a<-MfTQ?Se}!z(Pb+OEmlPlXX6Cx)+e=)XY`XB!9}e z>ahTd$kurp^Y+HImK()K+)kMznhPe)B7-3!ZQ4&p$Qse&%3=S_&;^T7F##kw6mapv{PhFL%T~gv|fw>*P{A ze&~Fb->0oJH!8w@=xo_Et7`aMlWcETG10MUF0aM9(8ffAt3|a=9tC=oYC@4I-Xi!7UfT8i?OQ4vID}i?q1}Fr4feA~pA{}A~YMYC$ zlIH|I52g*q)b_lHJsFd1jLcHG=mGi-t^#2`s+j`Eg6z*fg)j&1oA9z~rVjVugcow+^Mv+kSCV zVS{poSOw&il$7M6F*7nLzP$f(v){y_-GC47jZjBrAJZfMVWDf^)N>Q)PZ^+A9@bUz z)Qp|{+;2ec@f_^nrAMlB-SG1R)y=b?%l-Y3UvYEO&sjJ*wP-)h9ejmM0}+&TW7K&w zQ-S~-rp4$n^nwf=*Z0`24F2LF$U{)7Q(J)mPmR0Q^$?2>{3v#~40!$)!7x!^c<(U; z4{q2+BnPGMR1y=Hlr!yW`}X?Dn+YH{Vwc!Y27H*_SNsTuJpgqz4zO%SnWS;KHxPqZ zu%}ZW<;GyWRX88OUlFx&d!gVKO$%vKv#};VPJFMI)^Ge3fRidi?f3k&d&dIyLFSnPHD#*Ki4UapW>9l;n{x>skf7oP$BNu zGh@rOY;)xCSi1Y>2`B&(ooThM3XM!IEBt09@_a!@Mj-}6wUrTm?I0B`i`Nf-EWIQE zH`v57cNyyrNc5t5I>iq6OlTmgjhGGSavkU%ZeqS396Q-RUB~d2HTY@oiX+%%UhA{( zc=1l_ECKTp8>1;X#5GxO;!wt^-0vvlAR#XN00qx(mhFy73 zv~2y%`srbpiKUTsiqL_{j8kj=%$SfU8y)&UQ6@!mQW?*LHrZ%;df05TWygLT0tSno z3&p1uNM0P{^%LU+Y z7tK1va_bpfO>LBA2DMH0APSo$mlGnj=-$C(NSIf&jJ?28}vg=`S}bC%=E+f zX#g|=&}Bs;AU665{E|HPFa}^d>87kIa_fsSknXkP2|l>V9XjH^V~ct4Pt1)%i-Zl7 z)a-1X_U8SVnN5J0QfyaoROuWcxWz;KX=~}CdUcT{y1b@gM#^smTWryZeNqGY`ISs9 z3EmX|2So4td2+BSplLq)o$oskgWHNexj@FM_RgO)2n^!IxD8B!0G|C@@^j_rnrK1D zJx$-6MdEOlIj{V^QQtq}QUn9jHDgL|J+u5RfCcpUk?cYFO<4(i7MoGWjF{+fikECa zL@a_M_l`Jc$N@Vtycko~G5S+l*gZf`yD9pYvx%XSY#1nXV7(+@8?H}iYy5!e&c6zv zV>dclum9?pq6GZkfB*hJol*Xow*JrR%Y^}riAxG3 zunZDGfchbkF9(e{6@lzpu5-+(}NVsOKp(@)x3d-&qsgOmOcVz6s0xnM&4 z&@u`|h^OINw@gkOq-`Znfd6yP<-P<>Y~0t^eBb6~y!&f9GFZl@g&aXz>%7qhNG5}!#93usC^*WFl6_i9Ybm01)pR&dV4Be&Xt^c^ z57KXT_~zB$&x#ezvV7IBagL8>0f;pNht=cbuYim(+A(2$?PY8A4PfcJT7QBdt9ja# z)PPca`}2Le@mnZiHGC1!(g<e7|OZkm>}&;XJc%w-D%3m zyo-&IEoz+2a=;(3>I$4_c02DM+Z>4`-d`bv$JQ&OYAQ$?gF$RDgz5Xg&p<{Cfx^ax9-bmUra=&vvFWwtv7_ zf z!Qga$tJ!Yo^=m2VJ`UXqZ8jpuQ5--Rtx0P(Af3Yyh1|n5E1Zs~<>RAZnEOJ->Z2`Y z^n)?U!OQJDJ2SBmC(6~psE>F!mxeaSgshlThi%O*x8gbL={W#3#^D)E^)oz-zuFZovCEGS(%@V zK-t>F2~CEEUtD~DJ1YRv92MbG>FZz#%?oX4*l?EIh;iFiS$p_eKobpsRWG05a%=;C zJnqCA+Rs>FIl2F@=z#^_Z8A>A#-uZkMH(4f6_#h^0pK+2>!L3QACwtSj|gD1?=z^o@7YT3z1@$DYRT3w8Ark}xi}inCZz@n;!7+`>g7foy!7#h>gfv#_|O zZ7*VIVBw}v?Nt*9B)B+wDB#Fe@cC!s#KI{ss+1j@YRT}3C}p%T*B%PL{sz$6W{8od zlp@kKi^3=z2%KW*0uMrTje!B;>d|)7e}oO1%jIU$IC(UYq6YB39y}XO-PGmJhK|>I zzD~TOOTB`oS%=NI7_-xdU%ot+60v?_9jr>=5%qQ)C@w}sW2f}$N@V6{nVO;2*s7Ui z!Y2%;}ade+oPf2Wq>!pA@$&=*1DQ0ZqCnNMT!dpyQNI$HGy& z(BE5c>a*ZgJ$h_fq&O(>J{J~vZYz(O7x>|(3^a!z$)NJOirCOD))`hq19^0xdo>QT-!KuMMF5AwP(zxpCmp$ca>^p`|YFTY|4Kdx1TFrNO)x z3wkD^J(v^r9kE>!mgR+BQ48Un%3^muc;Ll zNjfP9e=6RH1Trq9PfenF!ZVtJN2S>5hRz*Q5~yfq2NKs1%#&u9P1NzIcHA_*G}+Y&|%5=9SE`=I*AV zSHbV27%IVSUZ$F{zqc$SfP>4Et{la+&=<`GH`FsTdhZ6|IR_lKXc^;|aVA8<(M^C_ zP?~g(C98rk_!I>Q``)psM*najms1!RbKBPktPCLWbfwDM7SL|lm|Feievf1ox5`>! zz>@^1*<0>yt7vI?R@>3Yh-;wAYMp?o@~D=MjG?r4&z{(+&Z%n;6|o82(ie2NUlrkt zCRQ{dR|zG1-gZ#>_=1OkWi`h2_12sSlyRX&#JiD!g~eujXmmxFd5f8a1*xSiC=tB+ z4uj{IC(u6UaZHz(=<_#=kI6$Z+oeuCR?G+SODVqM*&a~SJ&L67X2X5@VG(VwXeddk z>*ev_{KDm&0H;T*rgtm^5zM*&J1T(v>b8n?~Agpig6I;lu<95-HTV^voBTi3G zv2c3CGLemBp-J^N@I{V2BtKu8VTq+F3|6V$Q*=)tFDn7Q_?bEh4%aeR|CJyWpS0S% z@$0i$N{8vlW>xnmOTo`og2>?qAci7c7z}Ty0`vrY-UE*VZ{45hb8NU3e69S;LHG;cfeY|0!79 zMTkQ9iL}}yFAGXM>A9Lfm{U^K%iXQvP3jP!GlmFkTX?r4;CB4?aI@1-bV{wy?kNjq zs#s9QJka^3cAR+|TOf|;yxKxK_JwsiV`^~Ozh%Kt3$pdHE| z=L+O&I)a;#V&X&cF5NtHM#fOy^zh_0ckTMDcQsBvGlizqR0GMQdoy3UJF4DA#RWHa zOeBjV`oD;!c zCBdyFIE8@;mcn-IkJo|NtSgA<{Mx0BVa1J@ICD`U+*7Dou|^aDBAO6Rv6CT%Kl&=P zpKM3IJBW@L-NYP+4RlZor??y5;X9Ir3- zG7PwSL1{$78^@f+&wnA}mk<+MOZ$EMm+{f!F6{(^gIL($ORu9~GgV>UoW(%{Y|xH2 zy7YNm&KqBn>fTQG6`mrI>YJXHP0fB0^WDNJ2qa~Q?r<(*h^nsnYaX*CA@*ZkY|tvM z8fInel=juGZL>k>*|+xwo669qQ3c-U?`H|8dR#-H)>goy#NIHP+HpaaP)0QoyW2tQ zim?tNC*-&8^$$@-HLSbPchNsMJP~U=v<^spvXj&;+b>-Px+B0KJh?Y1j;L=Sesj*l zk8L}j64uEYpkx1#OuHz_Yu%ixYW>Fr&omBr&>}8enl&}zhXX?-1NT5mMCz_fWx}G0 z&o0Wl0vpjeY_G_`;zetV4bniSE4qDkMkb{fD+Gvf(0J}q^5nH2_uq=#+GvWAp^NU3 zg-;hyFIn@Py?9NFldXXZ-)+{U71P2139M#ItctxN=fbeY?$OPD#Whw8r%x zXb%{@x8ZMO4z8D`>8go{Iz_#tFBKDCl7+IyIV&;j_2a=8hk}Z3f4MyzaeH%63%=Ob zhpZ|jBch7f`D6<_CJd_J|G}1S@xwT2f4Ocv*Cdia=+ht=qO`Xd>1k;DYkYWDqE$#h zsn`2x8EJ!vzDB*QzM2sOsPS@*)4qi(+bceQgN`)#Vpel{?7(NcLCFJcI{-XRSqms1q5L+hFr5(2@Pi#{#+FY2AEvEZFK-t=mY&SyVM8<>H4PA9_1 z6%PM^bPTP#Z|{DlxI0Cv{VRR3^!gHdEyp!ol`zPJMb!V?bzn_ zj=urJEv&aN%r|b(jz1SQJ~@j)IhSvAAKW6pWo!E<eQU$XNmJ+mmK{r@P;8LA!6c2B~BIxwx?~ z2L0Radt=c%@qGpDukD9b{PfSo4Xp(J_U%suf#cu(E0o+g^lJJi6W>CAA&yoiP-XPj zMI$yL*wgabN~>Su)~(jZ|C3M4#6s+R+Fo+HP1=w#gx_zuB7niQ;t*)MKVSzT!lENC zIL4Y;maM&3#`oWSnyNDoxM3?}=TWP%q#q8~Isv8H{(xIz)_d00F`k9n-n9R91)Qv* zc@`qWa@(tcu-2|*rz1SXTA@dDEw`wg zz~sUro!+bFXoaRL*nC4tJI1E+H2yyxjvRJo$W_1hrMKTWzc4d-Ol+m$1=%~j=Uv;lPZzAHT1lwQ>@6=dP~YuWW|ZaxYaN= z6|(iwHHO2jef`7{=L^?fc4$JXq}TJLEB_gGH6^eYa>7tI=76lW@+ho;2r^h0$xY^ZA` zl&G@DruBmidN4tU)9$@P#gkCX7Bx=St74mn3wbJQpMWPUnjB`nkbZSp)Cc&f-rBr< zlaCAs*Pm}kG8gg;btYq=K9Bghd-|s6k@R)>q_XL`wr@lG_YSQUsL;eLc3k{N+$<+2&^g2ySCIHPY|saP4E48Q-STH#zWQi4M$0jxuEIK&rT@w3ua2pGkqT| z%@c!d^Y7Yhn6EzTv8*lZRi2_ckw{yrHr9{lNy#{@6cFZ*0W7X8$2Kzfs~R_p-283b z=w-Yz_}zWJmZ{nt8yF0Gjx~QEskPBxaA<7mOx%*Z+!P!2P*OmAsmPht6caa7#KG-_ zmZTE?NL!>y6 z*wnoAZZ>Kf<21OQ_MI_(2}HM}KjD*&ZKyJ2DLQ&d0(#<|*&9ln+4%Vi_4B(Ln)`y9 zKI+4en|T*uNUW^_suO?qWUT<6l9ozF8rK)pwUxY<=i}(?o(EywK1n0rVFgQ~?ymQ{ zyPkK~dT%YXU>1o?|L&<3Rz2Mixw5!wz9RCKfIh`Uo3*hbtmE_QBe-&h=DnILdE;8{ zl77~~A}($7zy2e&hTOH@js9-du;Ozgv5avy!IHNSh!{e`vQyE!OMrP4qpu7F z$8-0C)@5VuTfThk%1jkxdL&Fa#Kp&4M>XET`nz|Vh1gNc3rAj4Pe^Gz&%bQOXu#5a zuV#RO#96Z63VyiZSZxD??SRvo)-Bw%VvZb1ZG`DNI7@#pG)EJL2uVj#vuowKpf%zC zNGgUeRw?~>Xi`d#3O>zhH;LJIqKTKM#;Koqk-R}gW8rK%=Gk#CJK7rcQZ(Nr$~xBh z9k#zd=LSQ)iC7d)VlUt9aaH6X4}Z37#b~mX|EsSjc`we*4wNb*a&C_&1}i^@(eXgt z_wo{>U$RG*=xQsxBCnSAP_L$*ZQ)7AwtQ{DmexIn=H&B@VRU$t*Q@ZLs@_%Wb?5Pq z9&aZT)eos!94fk@C~jRQ8`~KS z>s^NmAsc}{q$QY`ab&p8<+LU9UbL@B^A>kZCrBb#K?8L9qSu^aoXKuoR=3a z<>0BVK7&UtJXeE-Jw&jSF?kUleQk5u^Sc9e58m5aaQ)YI>eyY@s9)5;5%2UYSjpg_ zAKu$4Arpd{e87w9Hk{PP;Yr6<hxPt>@2Pmo{Z({efEWsY4*}slLm)5CYpiU zL$54fvE>eYIw|0v@kn#?HX*NZv`#;(iX=dCJ=&=1dmm$=oRuFwZC*8z7ZnjKRPeJZ zaduWG5T?4yBLu2emuFTCD=^^M&vrUmtjJw_(X)HsJErEzTA~nWn&@>PR_M&ct7_+_ zsm$^CSJIR=%RbhJ7ksmWm>?}Kob0|t;Ystm>aS6r6TM~EqXc*tBU)D04~GZ|<0Iwm z9Sj{apaHN9)n)wTA^t#J|Dd?du@m;G$%77m@Ed!>GIVV^w; zFg%&@Yw^)b2!HXz^|UxaJuWPHyHoUVpfe=ym+}rd6X7u%8}?kL<5Hihfpz1@!{gr` z9KGnwD+?Rxi-bbR2+}V*$F+P%Mmk2_nnOt41GK|~S-|t#f+#n?(YJP%{NwBC%YvD? z@CSv%_Tx&CS|*{T0A>h)HEE=AL9!^i0Ayy#RMp7=`KFFOvzo)F5$BO*K?GJdeV^Y! zMZn@tN}!XV_Ur)oLu}_#FlWb$o`&9Qe&0v>*aLc&Dg@GSR}lBzrMVOw+Rw+g@t*FM zG5Y0Lei(NH8}W=SO1lM^I3|N%3V{4u*BFSCc3gyEqbyBa}G1j`bSGR+h}Pr3qqIPTyGb zwfs^h#%2{LhD9lC5{+{Mkhe`=(1MIB^)ZcWCsO|1PQ`E{s|rG2iE($sGytZ}R7sZ| zTI^_H5ne2gZz*d&AX_rcSZ29lEhoLJ9`B%t6WSD&>d|$gJQZIjt#&w_=QeLKA-%>s zbTzd`?DctQs<9eN6$gQ=J{E=vTe6a951S_l& z*|i#Pepyo-qtmCk8`|i4s>UN)({(rd{EIkZZ+cAA)y2`>u;>s9gRv=87IM322rA0Y zyy`uH{iijalnWTOt!*apGGv`;_e@*Ks9hI$|9HlL+G+z4%>p;%qO4Odv^ZGgfZ!2` zI})v@cipXEfm0~ZT*fW-;Dg<6eTBjddI`3%0=)4GrATd<$Pk&0t zH|t@54737av)ns7T}IO=LNSXxxA^%hd#DuYDF+Lu_>U^Aof*k%o$P@9)4I6N z-u-2Kkg*||U?Q!<;FT#I8BFtsAg}DKrwTmo2j+SnPr%x`1fK@qzD$-kA43j*8~Cu- zEdg?o%Y5O04_>$KvpaxL&$!#q7vLiw>ZeNBn6I9`Y#0;V(F;fCs+=){Li39~##}PB zDUn%=3ue2P0WgSc!Wll;Sz9_BXglQ)zW`(@`Wipth9kD{Z&b*^v|+Zl24(oNQap4oOk5YXm5zWxcHf zi_*BrPY^rlKC8P_W=J$?ZSBzZITEnGI%5cA%L)L{Otkx{YoCplovqlv%?{g7^tOi@ z_H(A0Sr!f}lE>h5zA*Lq)=kYYO2Y6kzrAaVU zg%$d`<7wKrFS{nUPM+Pc99YFuRqGac#|l_sY}*H`@wq`tGJL$GBbDff)3Uj%gzE-j z%S#xU@7729U2N4vm zq@7JaC~n}rma7SX;gPv8);o&@cV)lKnSwZpJ?cp6z!b6%Um2(8F4bt9e+tH8%r+fr z--0hXp5dTqP4YOux+OLEBi9H(poyIsJ);gx2!?icZ5@qwpnQ5tmS#pbYDI>LH1H>fd zRtyvZ+Cr^*ggiYze+6FB9Ydjf(*66-nltLm4>Frdq`W#mK_>d3s?Q^33Bj=>JNb2w zK&OKA7U~_&EfWQ(9}iJUJ}f;M%2JiRTC`#w_~1Q|lXd7zj$Bno?Sm$ymSd6Eartq_&T|&^0LfwZ{A}n-pb}MiC*% z*qO3DU%`ohTm^;bgJ4TP4x*Y$!_vdALAU%q`OYz;IA!_Gok=eEffVX|49d*>TzOdd^-BH#;eB^cS9SkF=gP&6T#HMQP;QR{T3c2wM`>OH!#V*tR|3vc) z)(k-|h9T!&w71d;|3OUr|BKr?{{W{jlDu^i`fp>;oQ$9+*H;FGcW&*b{!eZ)BjzJ% zFS)VV;oFUxv)MPDG2tCKDLnR=Jd0jD-g3qOT9bp;G2V#(CzfhCeC@{bdO+jW!u#Q2 zf;hOL05NW%xb_&nPl@8uoL$CzQBHcEg+mw0ZX*q~vbB;Jm)>Tym*_ zK3Rk0-l(XTB`dehg=%Xvls*Vm;W*mj;l2_gd<(K1{t!;lw2%t|6+Ve)OPT?>f4gCy zjK>!h!|wZ#9Sssj&ES=Qj>T==-2K`nC&%(M9!OoDHnE-QEm{k8^56@$lx5j|{88m< zN%x}h;N<B+t!*mSgrC2C&VNfxx4Jz zzrkVCeR4KF7m=Qx&_y1p>Uzg+BAvP5)kc7;$7;uV*qf52kNH($j1njO*0E)pJSB*R zqmoG!ZsUr;BT^yO2C2h5FFrx74YSOPHgK+(Bv{&a|EVlj8y2zDukGml*NopiNyA&X zZQ{Tx`-kK}uL(EeA55<|r2=+l9A&NTKrI`aTd_ZK1Xw-jsRbarSd+QJhxIuT`UVY1LTy<*6@nPB* z+v-`nJ|z)TO+iZH?Vx*ZnrZ!9TxPFeub}h~TI>ttt#;7v#oM{zCl_N@ii$pHKN_!L zlrb#rBaP%rJM-{<_gLo}spbAh5?72XXwCd~&Bk=z!q&&`m^6z+((pdgZz$pritz6o znGoPfHZ%%r*sBbEHDvs5)D=6OqxLa1QJ@#ew{ez4LZCY@RUo}#LZv^M;bkbauAdd5 z=bDrjL0veRl{FO>NLV7tEQ{1D7vpM&9LeZ-tAgp2T5ql(DW=fpUIPuSOS??e%W zDnWmSsS@4>yTpWauMlHALR>_R_}L1lcbC7BG7^YIlt}1yy3(9Z^T*8xW(=W>$>l3V z;{{}isB+b)wrk;wHH)2i7cAk=Ei_DHTWd;0lqyHBIPPS9AKF~21*i4P%S@GLxp8>4 z{(@-yB5(Z!XYMHO8!jU?4(T6E3Q&Jop{81say2AC3Ui~hT{_6y?z1!HELKr|bw1m%|9Qz0%|SC@FB?D9EDhl&UCEzzG0igSB#J_ zg+sQkJq}1ytKzasP3f$Pmhjcy?!}(-BxB#+YXkExqSUzX25j#SPN&WX-NGsQB&3@B zeA)w**F!VmR(P{3cmBaKD?(W2tAQtJfCr!QuM~9eN_mROr%D{WHd#xt zR*yi(?cPDcS)iyY1|2`PuDf3?IC0qySN4@yDDc_V@N$Y%x{rW1AC4EV{*K6Pn4BMC5TQDaL{SDpgG{K5cdIBiT zDAUKpxL30%{?3dI=_Y&ObZ(?FDv2#&W7Q)7mX7b6{db)Sky}9q)>dl$%0hZO5tTcH zoyqBR;&8obGJ=e_lAP+BAoV*G%#7Q?KmKdiQIB;9$7COj>zB}VpKsEl)rdWE8izRvDo8n zutSIq zUu4*z_#yhH5}N2!?G4B0Nv@CjhKHwOwzBbtAm&yZ`@{C%qyRPx$PmN+kmwbk$nX4ss-5uF%A9IS zFkl3xyUt_7DW(a)HEx&h!&|c?k3EdNla}3JFzT-S^DeJIYs&>B$A8SYVo!hZfPpY{nJ%lpStHXC=P8eJRb;!d2103dd5sGjYtJUg#1|{O+pie*_(RFl|a(GEQCL&sd{eKH`rYsN>JFJ)^mO#&NH=FEl z4VmscuhRz(a#_-EsG@PEn$6QiX`$tCwhK?F`y_|k-UY8^tVj3B#0;AiUNYgn4C*ns z?VJKaoh&Y{ZHX563B0d-EVwlXU-TAM%9eVI?mPcQzBj?$L7$217@7}R3oq@Y#P!Co{e0~EszjF>9RHl06GOfCvrE`Jy=D{UdabRk2&PwE{WY`s zj|C!rz3H606b>Y^niODC8W^V8CQfgEZNxI%Ed} z-o@PzH*3va5UbsXkp;5!NJ6K{r4MwE>}1vvdjb3-RhAZcs$CctLKnl9nxdK)!_gB! zogKn*RfnjB!`T~yscE7Tqm&~agMF1xV(S^LdFLrPyC|LaV~Pr?aU|zShTaP>5s}Qm2~O0q-iOat&_gGnNzjP*NA_;fVm;} zl8LLO2dw5d>~&>wlRKqQTUGL5$E@D1k!|AQCjtM2C*Ny<0ZaVa=MJGpvu!QXe=os3 zuhFhlY2o-DlTo6|rSBRy=N<8n!234*ts~;XujsPfCI8cMP(WLh= zmfpzfBQvi3)IjilU0cQJ3QrPdP&8=V3*BZfQ|HbU$$tk&wJ)27%M9?1&lk!wMLvRE zFd_?1R~luNMdJ-# zq2H26Q39eLK!x|0TGC*^0CHfGZEQ_5Vf+qV1b$BbSL>_5o+VVe3h+;#*L zJ?;vu?p01+4>Z6vr!!BsKS(fh9Q6)1WtF>F7T_s5w^vPh$JLiYf+T`b9T&j(a>hmN zORiF2;FZ`lhyb9Gyq_Fii%**HL$ZjE7Y%Q}Ui*&7jSTgxIUwCOSLr^aGGs0w3e z7E7PvZ5iyi-!alp2v*SO@Q#n^FN>Lo{}PGQvGKiDpB6BAmp@QJuQ|BVWHERlu_!Sk zLyAQ+aNpj!!9$u3qLG}_!e)Ie3visbXa8_j&P{k@EDh^?7F@2oK<2UVyu>Wxk!oqCc&H6i>(;}Q7&sE0^ai6*7@%Xl?_zfLB=4B8vv?gqO&yB7tGzf6 zPm5#HBeP4866ENL=XMmOahsIjw1Ghb-+urC`0(Ea0lW~VO8O@Qpkp=6yIyVR$cM!P zHxjr9_AkYm<&@JZSbz?1$%;r)1@;v4yNto^?rz?ig{ z?Ic9`{h<2njEAK<2z2wR+s;6%73WK>_T)a*`}z$KFlmL5j%Ctv{{tXMCh8NT$2o!4 zQeHs0+Rx3rRvNsL(T>_dc7N`bncld zc`SV$lBZq4`yA;wNeFldu2~SzomV<})hNQ+6Xd6B9Y1g*=p&y$vjw`C_Xc%TpKDB# z0R8kPO~mKl1s#33D5szRz97~@E{9y}Y1*B<{&*w*jX^00{hiz|oSVG7ZDdQXHgQVMcwZCB31U7|#+*76#bjEQ&8}BC&Gorv zbBsBQvKpg(>W}LomDT`;bi$#2BJrwJnM-jCFwY;EwC7w2lPeVITR2owh9H>%OL}LS z!&ppPizku87>1l7rYF2em64gi#y0oh^j?)g4lL`%hKrIZ$9+sx7Fhg(V`AHQ%S~d} zI8q6-ugSC!d2we!T}LWJ9f2dK4kb8R`C@Y{N9_@94ZkOf&Z~eUyjxEowV1|qM6o~z zzn-qOvgv>jeuJf3&J+muy=y_ZZ-O@z!oF~?_!PdDp+r%8rCnxrJYSbp`QjMA4{UTP z%bHc)!G=er5!n4RMC|lXh`;~i3k_!m%tyBkHQ(z?o~qT&@tncJZer37USx1tCkvpJ zB$yW3Z`%qv=#v%rVO+Rh93~WK`J}zrg(s2w-j+@nS)$kvUbOP|B*!wGVK&oBW%091 zF!@VtxG+r9tg@;t_lC6ln^8GCutH^K@Xl8-?E`dM^0tJ%$#T}9ye%s5A?ovES_x}w znt0pzL5}>pdS^CeMLw}ST;1Y z)Aif;cqcCD#~_(>+}eKf=D1H5(3J;m^Jc>*h!d1Qy1^W^^=e{+7eq~De3!h7R{H!q zPYXT-vC4reK7o}0=<)LKZeX+h zvMp({A1yCnT5HMDWmI)rK(RR6%kN@V-Xy~wwA-<;V4c+=~PSLD9EP8r3_fW837Bx88$GlmbOxQ z2TVg9rGKXE$PNjw%Pe;@57N;iS0}=YDN4tiqj((ZVw)Vw1rR2m?6W!O7&;K1UMZFq z`AEiWHLRe4|AgS7yHS*ZRk&nhvWUB{@2kEIb@hdW)tW9ui(`xk7i`R@WY+sa)mcqJ zLX=0!;ZuJ8)G6L(Qh7!kJc4f6dt7ZjQ8??)($MloH5-gU1-e7>(au!0tGjvV6Bd2P zmrE&Porg1A!M1YWRAeZKtV%LYp_j0_kh`N1(7WH-2*30yLyAjTIZJ%j2(1Yyrww3x zex+YAC;%F~%xXpV}GT!e0uUwCHXXrp^S<=34dIOW_S-SDZl zznU_&m7p*xfBcOvP=GeKB)oK1ziL)`-0}5b?KdCI?ktltZ6GBS`{ZMhR#Qh+D(000 zOMY}P`p}7*I;t#ka0uD(ZVl6vy&77&t~-j zs=;7n@s);Ob019M?UWoSqoRH(*f{jzas*>7O$1*T&n0xt1#plwLf7o|A{8E)HqO&L zl|+zx@S(h5Nr3czYyf`oL0`B?z2h9L7*#cfRs|1>9^u{U3GWiVt@I3fe?lqA!kk-V zB4RNrJ*=n}HFCo^xuhg0b^+IDWNX_lYLnGyMHtSV8qdnbTpO*V%Ce4zR(ZVaZtX6^-FkhEipzEO$Ca(0O!ZMC zZ^RIYBCZ;hSfATtBh?xVPdNo!f`r0;MY54gnlK3tk+wU{DAxjPX-H_1!|$?0t8@K8 zd2i+FrA46^Q>_bd(c2SF50?vh>57;YchbQ&ik!%i4%pbptdWk|b0!!za(!N6Rn;iv ze9dlL_@*a~iewCIh<*4xHQ#jSiuh}6_~6Nb%_@}9*u5VcXTrFoyE_kK>Qzk<9t0O( zDfT};djXmi1VsWpvym*3M0(#};?5day5zM#F1x2>N^Kedc{#7^ZXiTU-D#t{J>SlT z@?+=wM%VqqJBaBgdI~!&~Lk|YCFaYlmB?6;F8Y^ zmyx{)449{BE!}Etx^_q!h1pIWKd{xoIkF2(^DNU|uYE}$+rq(bZL37I=x&Pa7gp(? zx;~m7b#Af1NFX&GkCxmPEPBe^WGF*?$X9iqx*J&x!?ivGIQ$}zPxINzWHh$jFN(jTOxNHdHOU8lMZsSLzf2F|hsRuo4E#1f zFUhGed~4KFi#w5XiZ?7CHw2J{;+c2iT3|ppx7UxP21+k-!U-y}Z=;&7A|%wY$PrYq zen_J_#c?wj0vo`q4GQZ;`?>a2X9=`?mj2W&M4jf+8x4=V^{mPv0aIZY)L=7DO{m4T08UAaUya@_6)qq%dZ=t(L&)z&( z9>mN?jVrsGEl@wP(6`~%f2})-q8@A}{?>zGU_U7-tOdVcVu#hQ&5X=-*X>Xb-7t%; zF0#a+*tSi9D#<6A6rdoZX`Cn*u2y0mP|~= zHu2_QaA(#_EAi$1&&=|;cF_4IE5SN!$gKA@hm|qP{6Yp--aWqif!)U@wbpjoZvVy2 zmJQ1(UoDH#7$j3_YWZcct;Ks=+ej1fwUSG{8(Psw^b}zojK+Zu$F|c+x0JPsr z#A6(C%G$)aEk#1_X?|rL-iM064&?S-P*yh9)-1ZiK5%#c=m-^<6HnWHe`EgO1?#JL)aFXY`zW-l zpMcdD-+)T{t5>&Q>YD!Pt1!^FaXa5Ew#qh3*PDaW@Pw{3OU6B>#fEl3_rxY?0D@I%vl7=vpKPtsrm%P z`tjH;?{gI+|GXormYqE75)z7=R1W(o?Yz}xjeZZdL`%WM9_|8#JDCV~SxurH;d%TR zca9L{d@Nh3yltD5xah({I;X_eb!TuY4)R+^%q5hpx8kBVv+K9%(u%CaE_e8$4cq(a zxD9V*n}NlElo#!GS-VHgLjn-N7t0qFu$pwnp(P(Qp}XZD{|aGy^sNnHprP zF1--Njtn=Dy7-k?gdbSus8U3jc^q!MR=WbN&|{BSH(DW0{}X?(IM^5~!3v+aWf}!8 zoi5k$n>*cyJ$ZUvAPFH-%Jxc2m_K(PJT+ZudOIzbkrA^*zJboo+-VGU(PnCZN;JL3 zcaJ0xA3cc;6zlx?ZQ6nx!w}i%FkRbzvk7z4yFHmbt|FPgHM=1J7VHQA&P4Tk-f%pd zHUdR|rEIEROB*ggi!^)j8UJp_XCHg?1}sc+<^LXo*pT`r*jFW275B}KcfD77zo|OC zu{3UuP(3f;%WLaicF!)5rpGo&J!o}s5}3(pj}09Ag=Oox=FN%Utgk$*A{QT4h^spZ z{GW!;rDMMqznfm^>V)ah*JaB)`x41)-=z(Iv|32JiJ{P>-Qb6(RN9}i_|x*vUvzf< z(&hU9yEFckprA0b_xpDV@^9ZtRR3mI=r5l8i9Pgm { + config.set(Object.assign({}, karmaBaseConfig, { + webpack: karmaWebpackConfig + })) +}; diff --git a/package.json b/package.json new file mode 100644 index 00000000000..fd7d45f807d --- /dev/null +++ b/package.json @@ -0,0 +1,75 @@ +{ + "name": "@opentelemetry/propagator-jaeger", + "version": "0.3.2", + "description": "OpenTelemetry Jaeger propagator provides HTTP header propagation for systems that are using Jaeger HTTP header format.", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "repository": "open-telemetry/opentelemetry-js", + "scripts": { + "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.ts' --exclude 'test/index-webpack.ts'", + "test:browser": "nyc karma start --single-run", + "tdd": "yarn tdd:node", + "tdd:node": "yarn test -- --watch-extensions ts --watch", + "tdd:browser": "karma start", + "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../", + "clean": "rimraf build/*", + "check": "gts check", + "precompile": "tsc --version", + "compile": "npm run version:update && tsc -p .", + "fix": "gts fix", + "prepare": "npm run compile", + "version:update": "node ../../scripts/version-update.js", + "watch": "tsc -w" + }, + "keywords": [ + "opentelemetry", + "nodejs", + "tracing", + "profiling", + "jaeger" + ], + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + }, + "files": [ + "build/src/**/*.js", + "build/src/**/*.d.ts", + "doc", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@types/mocha": "^5.2.5", + "@types/node": "^12.6.8", + "@types/sinon": "^7.0.13", + "@types/webpack-env": "1.13.9", + "codecov": "^3.6.1", + "gts": "^1.1.0", + "istanbul-instrumenter-loader": "^3.0.1", + "karma": "^4.4.1", + "karma-chrome-launcher": "^3.1.0", + "karma-coverage-istanbul-reporter": "^2.1.0", + "karma-mocha": "^1.3.0", + "karma-spec-reporter": "^0.0.32", + "karma-webpack": "^4.0.2", + "mocha": "^6.1.0", + "nyc": "^14.1.1", + "rimraf": "^3.0.0", + "sinon": "^7.5.0", + "ts-loader": "^6.0.4", + "ts-mocha": "^6.0.0", + "ts-node": "^8.6.2", + "tslint-consistent-codestyle": "^1.16.0", + "tslint-microsoft-contrib": "^6.2.0", + "typescript": "3.7.2", + "webpack": "^4.35.2" + }, + "dependencies": { + "@opentelemetry/types": "^0.3.2" + } +} diff --git a/src/JaegerHttpTraceFormat.ts b/src/JaegerHttpTraceFormat.ts new file mode 100644 index 00000000000..811c6d9d410 --- /dev/null +++ b/src/JaegerHttpTraceFormat.ts @@ -0,0 +1,98 @@ +/*! + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SpanContext, HttpTextFormat, TraceFlags } from '@opentelemetry/types'; + +export const UBER_TRACE_ID_HEADER = 'uber-trace-id'; + +/** + * Propagates {@link SpanContext} through Trace Context format propagation. + * {trace-id}:{span-id}:{parent-span-id}:{flags} + * {trace-id} + * 64-bit or 128-bit random number in base16 format. + * Can be variable length, shorter values are 0-padded on the left. + * Value of 0 is invalid. + * {span-id} + * 64-bit random number in base16 format. + * {parent-span-id} + * Set to 0 because this field is deprecated. + * {flags} + * One byte bitmap, as two hex digits. + * Inspired by jaeger-client-node project. + */ +export class JaegerHttpTraceFormat implements HttpTextFormat { + private readonly _jaegerTraceHeader: string; + + /** + * @param {string} [customTraceHeader="uber-trace-id"] - HTTP header to inject\extract trace from. + **/ + constructor(customTraceHeader?: string) { + this._jaegerTraceHeader = customTraceHeader || UBER_TRACE_ID_HEADER; + } + + /** + * @param {SpanContext} spanContext - context from which we take information to inject in carrier. + * @param {string} format - unused. + * @param { [key: string]: unknown } carrier - a carrier to which span information will be injected. + **/ + inject( + spanContext: SpanContext, + format: string, + carrier: { [key: string]: unknown } + ) { + const traceFlags = `0${( + spanContext.traceFlags || TraceFlags.UNSAMPLED + ).toString(16)}`; + + carrier[ + this._jaegerTraceHeader + ] = `${spanContext.traceId}:${spanContext.spanId}:0:${traceFlags}`; + } + + /** + * @param {string} format - unused. + * @param { [key: string]: unknown } carrier - a carrier from which span context will be constructed. + * @return {SpanContext} - returns a span context extracted from carrier. + **/ + extract( + format: string, + carrier: { [key: string]: unknown } + ): SpanContext | null { + const uberTraceIdHeader = carrier[this._jaegerTraceHeader]; + if (!uberTraceIdHeader) return null; + const uberTraceId = Array.isArray(uberTraceIdHeader) + ? uberTraceIdHeader[0] + : uberTraceIdHeader; + + return deserializeSpanContext(uberTraceId); + } +} + +/** + * @param {string} serializedString - a serialized span context. + * @return {SpanContext} - returns a span context represented by the serializedString. + **/ +function deserializeSpanContext(serializedString: string): SpanContext | null { + let headers = serializedString.split(':'); + if (headers.length !== 4) { + return null; + } + const [traceId, spanId, , flags] = headers; + + const traceFlags = flags.match(/^[0-9a-f]{2}$/i) ? parseInt(flags) & 1 : 1; + + return { traceId, spanId, isRemote: true, traceFlags }; +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000000..620c254f237 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,17 @@ +/*! + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './JaegerHttpTraceFormat'; diff --git a/src/version.ts b/src/version.ts new file mode 100644 index 00000000000..2efbb00dcbe --- /dev/null +++ b/src/version.ts @@ -0,0 +1,18 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// this is autogenerated file, see scripts/version-update.js +export const VERSION = '0.3.2'; diff --git a/test/JaegerHttpTraceFormat.test.ts b/test/JaegerHttpTraceFormat.test.ts new file mode 100644 index 00000000000..a81dc92ffa8 --- /dev/null +++ b/test/JaegerHttpTraceFormat.test.ts @@ -0,0 +1,119 @@ +/*! + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import { + JaegerHttpTraceFormat, + UBER_TRACE_ID_HEADER, +} from '../src/JaegerHttpTraceFormat'; +import { SpanContext, TraceFlags } from '@opentelemetry/types'; + +describe('JaegerHttpTraceFormat', () => { + const jaegerHttpTraceFormat = new JaegerHttpTraceFormat(); + const customHeader = 'new-header'; + const customJaegerHttpTraceFormat = new JaegerHttpTraceFormat(customHeader); + let carrier: { [key: string]: unknown }; + + beforeEach(() => { + carrier = {}; + }); + + describe('.inject()', () => { + it('should set uber trace id header', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + + jaegerHttpTraceFormat.inject(spanContext, '', carrier); + assert.deepStrictEqual( + carrier[UBER_TRACE_ID_HEADER], + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01' + ); + }); + + it('should use custom header if provided', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + + customJaegerHttpTraceFormat.inject(spanContext, '', carrier); + assert.deepStrictEqual( + carrier[customHeader], + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01' + ); + }); + }); + + describe('.extract()', () => { + it('should extract context of a sampled span from carrier', () => { + carrier[UBER_TRACE_ID_HEADER] = + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; + const extractedSpanContext = jaegerHttpTraceFormat.extract('', carrier); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: '6e0c63257de34c92', + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('should extract context of a sampled span from carrier with 1 bit flag', () => { + carrier[UBER_TRACE_ID_HEADER] = + '9c41e35aeb6d1272:45fd2a9709dadcf1:a13699e3fb724f40:1'; + const extractedSpanContext = jaegerHttpTraceFormat.extract('', carrier); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: '45fd2a9709dadcf1', + traceId: '9c41e35aeb6d1272', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('should use custom header if provided', () => { + carrier[customHeader] = + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; + const extractedSpanContext = customJaegerHttpTraceFormat.extract( + '', + carrier + ); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: '6e0c63257de34c92', + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('returns null if UBER_TRACE_ID_HEADER header is missing', () => { + assert.deepStrictEqual(jaegerHttpTraceFormat.extract('', carrier), null); + }); + + it('returns null if UBER_TRACE_ID_HEADER header is invalid', () => { + carrier[UBER_TRACE_ID_HEADER] = 'invalid!'; + assert.deepStrictEqual( + jaegerHttpTraceFormat.extract('HttpTraceContext', carrier), + null + ); + }); + }); +}); diff --git a/test/index-webpack.ts b/test/index-webpack.ts new file mode 100644 index 00000000000..2709346bf14 --- /dev/null +++ b/test/index-webpack.ts @@ -0,0 +1,23 @@ +/*! + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is the webpack entry point for the browser Karma tests. It requires +// all modules ending in "test" from the current folder and all its subfolders. +const testsContext = require.context('.', true, /test$/); +testsContext.keys().forEach(testsContext); + +const srcContext = require.context('.', true, /src$/); +srcContext.keys().forEach(srcContext); diff --git a/tsconfig-release.json b/tsconfig-release.json new file mode 100644 index 00000000000..ffc0f77968b --- /dev/null +++ b/tsconfig-release.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": [] + }, + "include": ["src/**/*.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..a2042cd68b1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.base", + "compilerOptions": { + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000000..0710b135d07 --- /dev/null +++ b/tslint.json @@ -0,0 +1,4 @@ +{ + "rulesDirectory": ["node_modules/tslint-microsoft-contrib"], + "extends": ["../../tslint.base.js", "./node_modules/tslint-consistent-codestyle"] +}