PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/lib/node_modules/pm2/node_modules/@pm2/io
Viewing File: /usr/lib/node_modules/pm2/node_modules/@pm2/io/README.md
<div align="center"> <a href="http://pm2.keymetrics.io"> <img width=411px src="https://raw.githubusercontent.com/keymetrics/pm2-io-apm/master/pres/io-white.png"> </a> <br/> <br/> <br/> </div> The [@pm2/io](https://github.com/keymetrics/pm2-io-apm/tree/master/test) module comes along with PM2. It is the PM2 library responsible for gathering the metrics, reporting exceptions, exposing remote actions and every interaction with your application. You can also use it as a standalone agent, if you want to connect your nodejs process to PM2 Enterprise but without having to launch your application with PM2. # Table of Contents - [**Installation**](https://github.com/keymetrics/pm2-io-apm/tree/master#installation) - [**Expose Custom Metrics**](https://github.com/keymetrics/pm2-io-apm/tree/master#expose-custom-metrics) - [**Expose Remote Actions**](https://github.com/keymetrics/pm2-io-apm#expose-remote-actions-trigger-functions-remotely) - [**Report Custom Errors**](https://github.com/keymetrics/pm2-io-apm#report-user-error) - [**Distributed Tracing**](https://github.com/keymetrics/pm2-io-apm#distributed-tracing) - [**Configuration**](https://github.com/keymetrics/pm2-io-apm/tree/master#configuration) - [**Migration Guide**](https://github.com/keymetrics/pm2-io-apm#migration-guides) - [**Development**](https://github.com/keymetrics/pm2-io-apm/tree/master#development) - [**Notes**](https://github.com/keymetrics/pm2-io-apm/tree/master#notes) # Installation With npm: ```bash npm install @pm2/io --save ``` With yarn: ```bash yarn add @pm2/io ``` ## V8 Runtime Metrics To retrieve by default V8 Runtime metrics like: - V8 Garbage Collector metrics - [CPU Context Switch](https://unix.stackexchange.com/questions/442969/what-exactly-are-voluntary-context-switches) - [Page Fault](https://en.wikipedia.org/wiki/Page_fault#Types) Install: ```bash npm install @pm2/node-runtime-stats ``` And restart the application. ## Custom Metrics @pm2/io allows you to gather metrics from your code to be reported in the PM2 Plus/Enterprise dashboard. ### Create a custom metrics You can create a new custom metrics with the method `metric()` of `@pm2/io`. ```javascript const io = require('@pm2/io'); const users = io.metric({ name: 'Realtime user', }); users.set(10) ``` This arguments are available: - **name**: The metric name (required; string) - **id**: The type of metric (default 'metric', string) - **unit**: unit of the measure (default ''; string) - **historic**: keep the history in PM2 Plus (default: true; boolean) There are 4 different types of metrics: - **gauge**: To expose a variable's value - **counter**: A discrete counter to be triggered manually to count a number of occurrence - **meter**: To measure a frequency, a number of occurrences of a repeating event per unit of time - **histogram**: To measure a statistic, a statistic on a metric over the last 5 minutes ### Metric: Variable Exposition The first type of metric, called `metric`, allows to expose a variable's value. The variable can be exposed passively, with a function that gets called every second, or actively, with a method that you use to update the value. #### Active Mode In active mode, you need to create a probe and call the method `set()` to update the value. ```javascript const myMetric = io.metric({ name: 'Realtime Value' }); myMetric.set(23); ``` #### Passive Mode In passive mode you hust need to return the variable to be monitored: ```javascript const myMetric = io.metric({ name: 'Realtime Value', value: () => { return variable_to_monitor } }); ``` ### Counter: Discrete Counter The second type of metric, called `counter`, is a discrete counter that helps you count the number of occurrence of a particular event. The counter starts at 0 and can be incremented or decremented. ```javascript const io = require('@pm2/io'); const currentReq = io.counter({ name: 'Current req processed', type: 'counter', }); http.createServer((req, res) => { // Increment the counter, counter will eq 1 currentReq.inc(); req.on('end', () => { // Decrement the counter, counter will eq 0 currentReq.dec(); }); }); ``` ### Meter: Frequency The third type of metric, called `meter`, compute the frequency of an event. Each time the event happens, you need to call the `mark()` method. By default, the frequency is the number of events per second over the last minute. ```javascript const io = require('@pm2/io'); const reqsec = io.meter({ name: 'req/sec', type: 'meter', }); http.createServer((req, res) => { reqsec.mark(); res.end({ success: true }); }); ``` Additional options: - **samples**: (optional)(default: 1) Rate unit. Defaults to **1** sec. - **timeframe**: (optional)(default: 60) Timeframe over which the events will be analyzed. Defaults to **60** sec. ### Histogram: Statistics Collect values and provide statistic tools to explore their distribution over the last 5 minutes. ```javascript const io = require('@pm2/io'); const latency = io.histogram({ name: 'latency', measurement: 'mean' }); var latencyValue = 0; setInterval(() => { latencyValue = Math.round(Math.random() * 100); latency.update(latencyValue); }, 100); ``` Options are: - **measurement** : default: mean; min, max, sum, count, variance, mean, stddev, median, p75, p95, p99, p99. ## Expose Remote Actions: Trigger Functions remotely Remotely trigger functions from PM2 Plus or Enterprise. ### Simple actions The function takes a function as a parameter (cb here) and need to be called once the job is finished. Example: ```javascript const io = require('@pm2/io'); io.action('db:clean', (cb) => { clean.db(() => { // cb must be called at the end of the action return cb({ success: true }); }); }); ``` ## Report user error By default, in the Issue tab, you are only alerted for uncaught exceptions. Any exception that you catch is not reported. You can manually report them with the `notifyError()` method. ```javascript const io = require('@pm2/io'); io.notifyError(new Error('This is an error'), { // you can some http context that will be reported in the UI http: { url: req.url }, // or anything that you can like an user id custom: { user: req.user.id } }); ``` #### Express error reporting If you want you can configure your express middleware to automatically send you an error with the error middleware of express : ```javascript const io = require('@pm2/io') const express = require('express') const app = express() // add the routes that you want app.use('/toto', () => { throw new Error('ajdoijerr') }) // always add the middleware as the last one app.use(io.expressErrorHandler()) ``` #### Koa error reporting We also expose a custom koa middleware to report error with a specific koa middleware : ```javascript const io = require('@pm2/io') const Koa = require('koa') const app = new Koa() // the order isn't important with koa app.use(pmx.koaErrorHandler()) // add the routes that you want app.use(async ctx => { ctx.throw(new Error('toto')) }) ``` ## Distributed Tracing The Distributed Tracing allows to captures and propagates distributed traces through your system, allowing you to visualize how customer requests flow across services, rapidly perform deep root cause analysis, and better analyze latency across a highly distributed set of services. If you want to enable it, here the simple options to enable: ```javascript const io = require('@pm2/io').init({ tracing: { enabled: true, // will add the actual queries made to database, false by default detailedDatabasesCalls: true, // if you want you can ignore some endpoint based on their path ignoreIncomingPaths: [ // can be a regex /misc/, // or a exact string '/api/bucket' // or a function with the request (url, request) => { return true } ], // same as above but used to match entire URLs ignoreOutgoingUrls: [], /** * Determines the probability of a request to be traced. Ranges from 0.0 to 1.0 * default is 0.5 */ samplingRate: 0.5 } }) ``` By default we ignore specific incoming requests (you can override this by setting `ignoreIncomingPaths: []`): - Request with the OPTIONS or HEAD method - Request fetching a static ressources (`*.js`, `*.css`, `*.ico`, `*.svg`, `.png` or `*webpack*`) ### What's get traced When your application will receive a request from either `http`, `https` or `http2` it will start a trace. After that, we will trace the following modules: - `http` outgoing requests - `https` outgoing requests - `http2` outgoing requests - `mongodb-core` version 1 - 3 - `redis` versions > 2.6 - `ioredis` versions > 2.6 - `mysql` version 1 - 3 - `mysql2` version 1 - 3 - `pg` version > 6 - `vue-server-renderer` version 2 ### Custom Tracing API The custom tracing API can be used to create custom trace spans. A span is a particular unit of work within a trace, such as an RPC request. Spans may be nested; the outermost span is called a root span, even if there are no nested child spans. Root spans typically correspond to incoming requests, while child spans typically correspond to outgoing requests, or other work that is triggered in response to incoming requests. This means that root spans shouldn't be created in a context where a root span already exists; a child span is more suitable here. Instead, root spans should be created to track work that happens outside of the request lifecycle entirely, such as periodically scheduled work. To illustrate: ```js const io = require('@pm2/io').init({ tracing: true }) const tracer = io.getTracer() // ... app.get('/:token', function (req, res) { const token = req.params.token // the '2' correspond to the type of operation you want to trace // can be 0 (UNKNOWN), 1 (SERVER) or 2 (CLIENT) // 'verifyToken' here will be the name of the operation const customSpan = tracer.startChildSpan('verifyToken', 2) // note that customSpan can be null if you are not inside a request req.Token.verifyToken(token, (err, result) => { if (err) { // you can add tags to the span to attach more details to the span customSpan.addAttribute('error', err.message) customSpan.end() return res.status(500).send('error') } customSpan.addAttribute('result', result) // be sure to always .end() the spans customSpan.end() // redirect the user if the token is valid res.send('/user/me') }) }) // For any significant work done _outside_ of the request lifecycle, use // startRootSpan. const traceOptions = { name: 'my custom trace', // the '1' correspond to the type of operation you want to trace // can be 0 (UNKNOWN), 1 (SERVER) or 2 (CLIENT) kind: '1' } plugin.tracer.startRootSpan(traceOptions, rootSpan => { // ... // Be sure to call rootSpan.end(). rootSpan.end() }); ``` ## Configuration ### Global configuration object ```javascript export class IOConfig { /** * Automatically catch unhandled errors */ catchExceptions?: boolean = true /** * Configure the metrics to add automatically to your process */ metrics?: { eventLoop: boolean = true, network: boolean = false, http: boolean = true, gc: boolean = true, v8: boolean = true } /** * Configure the default actions that you can run */ actions?: { eventLoopDump?: boolean = true } /** * Configure availables profilers that will be exposed */ profiling?: { /** * Toggle the CPU profiling actions */ cpuJS: boolean = true /** * Toggle the heap snapshot actions */ heapSnapshot: boolean = true /** * Toggle the heap sampling actions */ heapSampling: boolean = true /** * Force a specific implementation of profiler * * available: * - 'addon' (using the v8-profiler-node8 addon) * - 'inspector' (using the "inspector" api from node core) * - 'none' (disable the profilers) * - 'both' (will try to use inspector and fallback on addon if available) */ implementation: string = 'both' } /** * Configure the transaction tracing options */ tracing?: { /** * Enabled the distributed tracing feature. */ enabled: boolean /** * If you want to report a specific service name * the default is the same as in apmOptions */ serviceName?: string /** * Generate trace for outgoing request that aren't connected to a incoming one * default is false */ outbound?: boolean /** * Determines the probability of a request to be traced. Ranges from 0.0 to 1.0 * default is 0.5 */ samplingRate?: number, /** * Add details about databases calls (redis, mongodb etc) */ detailedDatabasesCalls?: boolean, /** * Ignore specific incoming request depending on their path */ ignoreIncomingPaths?: Array<IgnoreMatcher<httpModule.IncomingMessage>> /** * Ignore specific outgoing request depending on their url */ ignoreOutgoingUrls?: Array<IgnoreMatcher<httpModule.ClientRequest>> /** * Set to true when wanting to create span for raw TCP connection * instead of new http request */ createSpanWithNet: boolean } /** * If you want to connect to PM2 Enterprise without using PM2, you should enable * the standalone mode * * default is false */ standalone?: boolean = false /** * Define custom options for the standalone mode */ apmOptions?: { /** * public key of the bucket to which the agent need to connect */ publicKey: string /** * Secret key of the bucket to which the agent need to connect */ secretKey: string /** * The name of the application/service that will be reported to PM2 Enterprise */ appName: string /** * The name of the server as reported in PM2 Enterprise * * default is os.hostname() */ serverName?: string /** * Broadcast all the logs from your application to our backend */ sendLogs?: Boolean /** * Avoid to broadcast any logs from your application to our backend * Even if the sendLogs option set to false, you can still see some logs * when going to the log interface (it automatically trigger broacasting log) */ disableLogs?: Boolean /** * Since logs can be forwared to our backend you may want to ignore specific * logs (containing sensitive data for example) */ logFilter?: string | RegExp /** * Proxy URI to use when reaching internet * Supporting socks5,http,https,pac,socks4 * see https://github.com/TooTallNate/node-proxy-agent * * example: socks5://username:password@some-socks-proxy.com:9050 */ proxy?: string } } ``` You can pass whatever options you want to `io.init`, it will automatically update its configuration. ## Migration Guides ### 2.x to 3.x Here the list of breaking changes : - Removed `io.scopedAction` because of low user adoption - Removed `io.notify` in favor of `io.notifyError` (droppin replacement) - Removed support for `gc-stats` module - Removed Heap profiling support when using the profiler addon (which wasn't possible at all) - Removed deep-metrics support (the module that allowed to get metrics about websocket/mongo out of the box), we are working on a better solution. - Removed `io.transpose` - Removed `io.probe()` to init metrics - **Changed the configuration structure** High chance that if you used a custom configuration for `io.init`, you need to change it to reflect the new configuration. Apart from that and the `io.notify` removal, it shouldn't break the way you instanciated metrics. If you find something else that breaks please report it to us (tech@keymetrics.io). ### 3.x to 4.x The only difference with the 4.x version is the new tracing system put in place, so the only changs are related to it: - **Dropped the support for node 4** (you can still use the 3.x if you use node 4 but you will not have access to the distributed tracing) - **Changed the tracing configuration** (see options above) ## Development To auto rebuild on file change: ```bash $ npm install $ npm run watch ``` To test only one file: ```bash $ npm run unit <typescript-file-to-test.ts> ``` Run transpilation + test + coverage: ```bash $ npm run test ``` Run transpilation + test only: ```bash $ npm run unit <test> ``` ## Notes Curently this package isn't compatible with `amqp` if you use the `network` metrics. We recommend to disable the metrics with the following configuration in this case : ```javascript io.init({ metrics: { network: false } }) ```