@devmoods/observability 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/coverage/clover.xml +18 -4
- package/coverage/coverage-final.json +1 -1
- package/coverage/index.html +9 -9
- package/coverage/index.ts.html +198 -6
- package/dist/index.d.ts +31 -0
- package/dist/index.js +38 -0
- package/package.json +4 -3
- package/src/__tests__/index.test.ts +116 -1
- package/src/index.ts +72 -0
package/coverage/clover.xml
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<coverage generated="
|
|
3
|
-
<project timestamp="
|
|
4
|
-
<metrics statements="
|
|
2
|
+
<coverage generated="1776355283462" clover="3.2.0">
|
|
3
|
+
<project timestamp="1776355283462" name="All files">
|
|
4
|
+
<metrics statements="19" coveredstatements="19" conditionals="1" coveredconditionals="1" methods="10" coveredmethods="10" elements="30" coveredelements="30" complexity="0" loc="19" ncloc="19" packages="1" files="1" classes="1"/>
|
|
5
5
|
<file name="index.ts" path="/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts">
|
|
6
|
-
<metrics statements="
|
|
6
|
+
<metrics statements="19" coveredstatements="19" conditionals="1" coveredconditionals="1" methods="10" coveredmethods="10"/>
|
|
7
7
|
<line num="5" count="1" type="stmt"/>
|
|
8
8
|
<line num="6" count="1" type="stmt"/>
|
|
9
9
|
<line num="11" count="1" type="stmt"/>
|
|
10
10
|
<line num="17" count="3" type="stmt"/>
|
|
11
11
|
<line num="18" count="3" type="stmt"/>
|
|
12
|
+
<line num="52" count="8" type="stmt"/>
|
|
13
|
+
<line num="55" count="7" type="stmt"/>
|
|
14
|
+
<line num="56" count="7" type="stmt"/>
|
|
15
|
+
<line num="57" count="2" type="stmt"/>
|
|
16
|
+
<line num="62" count="3" type="stmt"/>
|
|
17
|
+
<line num="66" count="3" type="stmt"/>
|
|
18
|
+
<line num="67" count="4" type="stmt"/>
|
|
19
|
+
<line num="72" count="1" type="stmt"/>
|
|
20
|
+
<line num="73" count="1" type="stmt"/>
|
|
21
|
+
<line num="78" count="1" type="stmt"/>
|
|
22
|
+
<line num="79" count="1" type="stmt"/>
|
|
23
|
+
<line num="84" count="1" type="stmt"/>
|
|
24
|
+
<line num="85" count="1" type="stmt"/>
|
|
25
|
+
<line num="90" count="1" type="stmt"/>
|
|
12
26
|
</file>
|
|
13
27
|
</project>
|
|
14
28
|
</coverage>
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
{"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts": {"path":"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts","statementMap":{"0":{"start":{"line":5,"column":14},"end":{"line":5,"column":30}},"1":{"start":{"line":6,"column":2},"end":{"line":20,"column":null}},"2":{"start":{"line":11,"column":6},"end":{"line":11,"column":null}},"3":{"start":{"line":17,"column":22},"end":{"line":17,"column":43}},"4":{"start":{"line":18,"column":6},"end":{"line":18,"column":null}}},"fnMap":{"0":{"name":"startTimer","decl":{"start":{"line":4,"column":16},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":29},"end":{"line":20,"column":null}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":12}},"loc":{"start":{"line":10,"column":12},"end":{"line":11,"column":null}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":16,"column":4},"end":{"line":16,"column":18}},"loc":{"start":{"line":16,"column":18},"end":{"line":18,"column":null}},"line":16}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":3,"4":3},"f":{"0":1,"1":1,"2":3},"b":{},"meta":{"lastBranch":
|
|
1
|
+
{"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts": {"path":"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/observability/src/index.ts","statementMap":{"0":{"start":{"line":5,"column":14},"end":{"line":5,"column":30}},"1":{"start":{"line":6,"column":2},"end":{"line":20,"column":null}},"2":{"start":{"line":11,"column":6},"end":{"line":11,"column":null}},"3":{"start":{"line":17,"column":22},"end":{"line":17,"column":43}},"4":{"start":{"line":18,"column":6},"end":{"line":18,"column":null}},"5":{"start":{"line":52,"column":23},"end":{"line":52,"column":48}},"6":{"start":{"line":55,"column":4},"end":{"line":55,"column":null}},"7":{"start":{"line":56,"column":4},"end":{"line":57,"column":null}},"8":{"start":{"line":57,"column":6},"end":{"line":57,"column":null}},"9":{"start":{"line":62,"column":4},"end":{"line":62,"column":null}},"10":{"start":{"line":66,"column":4},"end":{"line":67,"column":null}},"11":{"start":{"line":67,"column":6},"end":{"line":67,"column":null}},"12":{"start":{"line":72,"column":4},"end":{"line":73,"column":null}},"13":{"start":{"line":73,"column":6},"end":{"line":73,"column":null}},"14":{"start":{"line":78,"column":4},"end":{"line":79,"column":null}},"15":{"start":{"line":79,"column":6},"end":{"line":79,"column":null}},"16":{"start":{"line":84,"column":4},"end":{"line":85,"column":null}},"17":{"start":{"line":85,"column":6},"end":{"line":85,"column":null}},"18":{"start":{"line":90,"column":23},"end":{"line":90,"column":36}}},"fnMap":{"0":{"name":"startTimer","decl":{"start":{"line":4,"column":16},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":29},"end":{"line":20,"column":null}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":12}},"loc":{"start":{"line":10,"column":12},"end":{"line":11,"column":null}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":16,"column":4},"end":{"line":16,"column":18}},"loc":{"start":{"line":16,"column":18},"end":{"line":18,"column":null}},"line":16},"3":{"name":"(anonymous_3)","decl":{"start":{"line":54,"column":2},"end":{"line":54,"column":12}},"loc":{"start":{"line":54,"column":49},"end":{"line":57,"column":null}},"line":54},"4":{"name":"(anonymous_4)","decl":{"start":{"line":56,"column":4},"end":{"line":56,"column":17}},"loc":{"start":{"line":56,"column":17},"end":{"line":57,"column":null}},"line":56},"5":{"name":"(anonymous_5)","decl":{"start":{"line":61,"column":2},"end":{"line":61,"column":28}},"loc":{"start":{"line":61,"column":28},"end":{"line":62,"column":null}},"line":61},"6":{"name":"(anonymous_6)","decl":{"start":{"line":65,"column":2},"end":{"line":65,"column":12}},"loc":{"start":{"line":65,"column":74},"end":{"line":67,"column":null}},"line":65},"7":{"name":"(anonymous_7)","decl":{"start":{"line":71,"column":2},"end":{"line":71,"column":8}},"loc":{"start":{"line":71,"column":74},"end":{"line":73,"column":null}},"line":71},"8":{"name":"(anonymous_8)","decl":{"start":{"line":77,"column":2},"end":{"line":77,"column":15}},"loc":{"start":{"line":77,"column":81},"end":{"line":79,"column":null}},"line":77},"9":{"name":"(anonymous_9)","decl":{"start":{"line":83,"column":2},"end":{"line":83,"column":9}},"loc":{"start":{"line":83,"column":75},"end":{"line":85,"column":null}},"line":83}},"branchMap":{"0":{"loc":{"start":{"line":65,"column":26},"end":{"line":65,"column":37}},"type":"default-arg","locations":[{"start":{"line":65,"column":34},"end":{"line":65,"column":37}}],"line":65}},"s":{"0":1,"1":1,"2":1,"3":3,"4":3,"5":8,"6":7,"7":7,"8":2,"9":3,"10":3,"11":4,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1},"f":{"0":1,"1":1,"2":3,"3":7,"4":2,"5":3,"6":3,"7":1,"8":1,"9":1},"b":{"0":[3]},"meta":{"lastBranch":1,"lastFunction":10,"lastStatement":19,"seen":{"f:4:16:4:29":0,"s:5:14:5:30":0,"s:6:2:20:Infinity":1,"f:10:4:10:12":1,"s:11:6:11:Infinity":2,"f:16:4:16:18":2,"s:17:22:17:43":3,"s:18:6:18:Infinity":4,"s:52:23:52:48":5,"f:54:2:54:12":3,"s:55:4:55:Infinity":6,"s:56:4:57:Infinity":7,"f:56:4:56:17":4,"s:57:6:57:Infinity":8,"f:61:2:61:28":5,"s:62:4:62:Infinity":9,"f:65:2:65:12":6,"b:65:34:65:37":0,"s:66:4:67:Infinity":10,"s:67:6:67:Infinity":11,"f:71:2:71:8":7,"s:72:4:73:Infinity":12,"s:73:6:73:Infinity":13,"f:77:2:77:15":8,"s:78:4:79:Infinity":14,"s:79:6:79:Infinity":15,"f:83:2:83:9":9,"s:84:4:85:Infinity":16,"s:85:6:85:Infinity":17,"s:90:23:90:36":18}}}
|
|
2
2
|
}
|
package/coverage/index.html
CHANGED
|
@@ -25,28 +25,28 @@
|
|
|
25
25
|
<div class='fl pad1y space-right2'>
|
|
26
26
|
<span class="strong">100% </span>
|
|
27
27
|
<span class="quiet">Statements</span>
|
|
28
|
-
<span class='fraction'>
|
|
28
|
+
<span class='fraction'>19/19</span>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
<div class='fl pad1y space-right2'>
|
|
33
33
|
<span class="strong">100% </span>
|
|
34
34
|
<span class="quiet">Branches</span>
|
|
35
|
-
<span class='fraction'>
|
|
35
|
+
<span class='fraction'>1/1</span>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
<div class='fl pad1y space-right2'>
|
|
40
40
|
<span class="strong">100% </span>
|
|
41
41
|
<span class="quiet">Functions</span>
|
|
42
|
-
<span class='fraction'>
|
|
42
|
+
<span class='fraction'>10/10</span>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
<div class='fl pad1y space-right2'>
|
|
47
47
|
<span class="strong">100% </span>
|
|
48
48
|
<span class="quiet">Lines</span>
|
|
49
|
-
<span class='fraction'>
|
|
49
|
+
<span class='fraction'>19/19</span>
|
|
50
50
|
</div>
|
|
51
51
|
|
|
52
52
|
|
|
@@ -84,13 +84,13 @@
|
|
|
84
84
|
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
|
85
85
|
</td>
|
|
86
86
|
<td data-value="100" class="pct high">100%</td>
|
|
87
|
-
<td data-value="
|
|
87
|
+
<td data-value="19" class="abs high">19/19</td>
|
|
88
88
|
<td data-value="100" class="pct high">100%</td>
|
|
89
|
-
<td data-value="
|
|
89
|
+
<td data-value="1" class="abs high">1/1</td>
|
|
90
90
|
<td data-value="100" class="pct high">100%</td>
|
|
91
|
-
<td data-value="
|
|
91
|
+
<td data-value="10" class="abs high">10/10</td>
|
|
92
92
|
<td data-value="100" class="pct high">100%</td>
|
|
93
|
-
<td data-value="
|
|
93
|
+
<td data-value="19" class="abs high">19/19</td>
|
|
94
94
|
</tr>
|
|
95
95
|
|
|
96
96
|
</tbody>
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
102
102
|
Code coverage generated by
|
|
103
103
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
104
|
-
at 2026-04-
|
|
104
|
+
at 2026-04-16T16:01:23.455Z
|
|
105
105
|
</div>
|
|
106
106
|
<script src="prettify.js"></script>
|
|
107
107
|
<script>
|
package/coverage/index.ts.html
CHANGED
|
@@ -25,28 +25,28 @@
|
|
|
25
25
|
<div class='fl pad1y space-right2'>
|
|
26
26
|
<span class="strong">100% </span>
|
|
27
27
|
<span class="quiet">Statements</span>
|
|
28
|
-
<span class='fraction'>
|
|
28
|
+
<span class='fraction'>19/19</span>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
<div class='fl pad1y space-right2'>
|
|
33
33
|
<span class="strong">100% </span>
|
|
34
34
|
<span class="quiet">Branches</span>
|
|
35
|
-
<span class='fraction'>
|
|
35
|
+
<span class='fraction'>1/1</span>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
<div class='fl pad1y space-right2'>
|
|
40
40
|
<span class="strong">100% </span>
|
|
41
41
|
<span class="quiet">Functions</span>
|
|
42
|
-
<span class='fraction'>
|
|
42
|
+
<span class='fraction'>10/10</span>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
<div class='fl pad1y space-right2'>
|
|
47
47
|
<span class="strong">100% </span>
|
|
48
48
|
<span class="quiet">Lines</span>
|
|
49
|
-
<span class='fraction'>
|
|
49
|
+
<span class='fraction'>19/19</span>
|
|
50
50
|
</div>
|
|
51
51
|
|
|
52
52
|
|
|
@@ -89,7 +89,71 @@
|
|
|
89
89
|
<a name='L24'></a><a href='#L24'>24</a>
|
|
90
90
|
<a name='L25'></a><a href='#L25'>25</a>
|
|
91
91
|
<a name='L26'></a><a href='#L26'>26</a>
|
|
92
|
-
<a name='L27'></a><a href='#L27'>27</a
|
|
92
|
+
<a name='L27'></a><a href='#L27'>27</a>
|
|
93
|
+
<a name='L28'></a><a href='#L28'>28</a>
|
|
94
|
+
<a name='L29'></a><a href='#L29'>29</a>
|
|
95
|
+
<a name='L30'></a><a href='#L30'>30</a>
|
|
96
|
+
<a name='L31'></a><a href='#L31'>31</a>
|
|
97
|
+
<a name='L32'></a><a href='#L32'>32</a>
|
|
98
|
+
<a name='L33'></a><a href='#L33'>33</a>
|
|
99
|
+
<a name='L34'></a><a href='#L34'>34</a>
|
|
100
|
+
<a name='L35'></a><a href='#L35'>35</a>
|
|
101
|
+
<a name='L36'></a><a href='#L36'>36</a>
|
|
102
|
+
<a name='L37'></a><a href='#L37'>37</a>
|
|
103
|
+
<a name='L38'></a><a href='#L38'>38</a>
|
|
104
|
+
<a name='L39'></a><a href='#L39'>39</a>
|
|
105
|
+
<a name='L40'></a><a href='#L40'>40</a>
|
|
106
|
+
<a name='L41'></a><a href='#L41'>41</a>
|
|
107
|
+
<a name='L42'></a><a href='#L42'>42</a>
|
|
108
|
+
<a name='L43'></a><a href='#L43'>43</a>
|
|
109
|
+
<a name='L44'></a><a href='#L44'>44</a>
|
|
110
|
+
<a name='L45'></a><a href='#L45'>45</a>
|
|
111
|
+
<a name='L46'></a><a href='#L46'>46</a>
|
|
112
|
+
<a name='L47'></a><a href='#L47'>47</a>
|
|
113
|
+
<a name='L48'></a><a href='#L48'>48</a>
|
|
114
|
+
<a name='L49'></a><a href='#L49'>49</a>
|
|
115
|
+
<a name='L50'></a><a href='#L50'>50</a>
|
|
116
|
+
<a name='L51'></a><a href='#L51'>51</a>
|
|
117
|
+
<a name='L52'></a><a href='#L52'>52</a>
|
|
118
|
+
<a name='L53'></a><a href='#L53'>53</a>
|
|
119
|
+
<a name='L54'></a><a href='#L54'>54</a>
|
|
120
|
+
<a name='L55'></a><a href='#L55'>55</a>
|
|
121
|
+
<a name='L56'></a><a href='#L56'>56</a>
|
|
122
|
+
<a name='L57'></a><a href='#L57'>57</a>
|
|
123
|
+
<a name='L58'></a><a href='#L58'>58</a>
|
|
124
|
+
<a name='L59'></a><a href='#L59'>59</a>
|
|
125
|
+
<a name='L60'></a><a href='#L60'>60</a>
|
|
126
|
+
<a name='L61'></a><a href='#L61'>61</a>
|
|
127
|
+
<a name='L62'></a><a href='#L62'>62</a>
|
|
128
|
+
<a name='L63'></a><a href='#L63'>63</a>
|
|
129
|
+
<a name='L64'></a><a href='#L64'>64</a>
|
|
130
|
+
<a name='L65'></a><a href='#L65'>65</a>
|
|
131
|
+
<a name='L66'></a><a href='#L66'>66</a>
|
|
132
|
+
<a name='L67'></a><a href='#L67'>67</a>
|
|
133
|
+
<a name='L68'></a><a href='#L68'>68</a>
|
|
134
|
+
<a name='L69'></a><a href='#L69'>69</a>
|
|
135
|
+
<a name='L70'></a><a href='#L70'>70</a>
|
|
136
|
+
<a name='L71'></a><a href='#L71'>71</a>
|
|
137
|
+
<a name='L72'></a><a href='#L72'>72</a>
|
|
138
|
+
<a name='L73'></a><a href='#L73'>73</a>
|
|
139
|
+
<a name='L74'></a><a href='#L74'>74</a>
|
|
140
|
+
<a name='L75'></a><a href='#L75'>75</a>
|
|
141
|
+
<a name='L76'></a><a href='#L76'>76</a>
|
|
142
|
+
<a name='L77'></a><a href='#L77'>77</a>
|
|
143
|
+
<a name='L78'></a><a href='#L78'>78</a>
|
|
144
|
+
<a name='L79'></a><a href='#L79'>79</a>
|
|
145
|
+
<a name='L80'></a><a href='#L80'>80</a>
|
|
146
|
+
<a name='L81'></a><a href='#L81'>81</a>
|
|
147
|
+
<a name='L82'></a><a href='#L82'>82</a>
|
|
148
|
+
<a name='L83'></a><a href='#L83'>83</a>
|
|
149
|
+
<a name='L84'></a><a href='#L84'>84</a>
|
|
150
|
+
<a name='L85'></a><a href='#L85'>85</a>
|
|
151
|
+
<a name='L86'></a><a href='#L86'>86</a>
|
|
152
|
+
<a name='L87'></a><a href='#L87'>87</a>
|
|
153
|
+
<a name='L88'></a><a href='#L88'>88</a>
|
|
154
|
+
<a name='L89'></a><a href='#L89'>89</a>
|
|
155
|
+
<a name='L90'></a><a href='#L90'>90</a>
|
|
156
|
+
<a name='L91'></a><a href='#L91'>91</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
93
157
|
<span class="cline-any cline-neutral"> </span>
|
|
94
158
|
<span class="cline-any cline-neutral"> </span>
|
|
95
159
|
<span class="cline-any cline-neutral"> </span>
|
|
@@ -115,6 +179,70 @@
|
|
|
115
179
|
<span class="cline-any cline-neutral"> </span>
|
|
116
180
|
<span class="cline-any cline-neutral"> </span>
|
|
117
181
|
<span class="cline-any cline-neutral"> </span>
|
|
182
|
+
<span class="cline-any cline-neutral"> </span>
|
|
183
|
+
<span class="cline-any cline-neutral"> </span>
|
|
184
|
+
<span class="cline-any cline-neutral"> </span>
|
|
185
|
+
<span class="cline-any cline-neutral"> </span>
|
|
186
|
+
<span class="cline-any cline-neutral"> </span>
|
|
187
|
+
<span class="cline-any cline-neutral"> </span>
|
|
188
|
+
<span class="cline-any cline-neutral"> </span>
|
|
189
|
+
<span class="cline-any cline-neutral"> </span>
|
|
190
|
+
<span class="cline-any cline-neutral"> </span>
|
|
191
|
+
<span class="cline-any cline-neutral"> </span>
|
|
192
|
+
<span class="cline-any cline-neutral"> </span>
|
|
193
|
+
<span class="cline-any cline-neutral"> </span>
|
|
194
|
+
<span class="cline-any cline-neutral"> </span>
|
|
195
|
+
<span class="cline-any cline-neutral"> </span>
|
|
196
|
+
<span class="cline-any cline-neutral"> </span>
|
|
197
|
+
<span class="cline-any cline-neutral"> </span>
|
|
198
|
+
<span class="cline-any cline-neutral"> </span>
|
|
199
|
+
<span class="cline-any cline-neutral"> </span>
|
|
200
|
+
<span class="cline-any cline-neutral"> </span>
|
|
201
|
+
<span class="cline-any cline-neutral"> </span>
|
|
202
|
+
<span class="cline-any cline-neutral"> </span>
|
|
203
|
+
<span class="cline-any cline-neutral"> </span>
|
|
204
|
+
<span class="cline-any cline-neutral"> </span>
|
|
205
|
+
<span class="cline-any cline-neutral"> </span>
|
|
206
|
+
<span class="cline-any cline-neutral"> </span>
|
|
207
|
+
<span class="cline-any cline-yes">8x</span>
|
|
208
|
+
<span class="cline-any cline-neutral"> </span>
|
|
209
|
+
<span class="cline-any cline-neutral"> </span>
|
|
210
|
+
<span class="cline-any cline-yes">7x</span>
|
|
211
|
+
<span class="cline-any cline-yes">7x</span>
|
|
212
|
+
<span class="cline-any cline-yes">2x</span>
|
|
213
|
+
<span class="cline-any cline-neutral"> </span>
|
|
214
|
+
<span class="cline-any cline-neutral"> </span>
|
|
215
|
+
<span class="cline-any cline-neutral"> </span>
|
|
216
|
+
<span class="cline-any cline-neutral"> </span>
|
|
217
|
+
<span class="cline-any cline-yes">3x</span>
|
|
218
|
+
<span class="cline-any cline-neutral"> </span>
|
|
219
|
+
<span class="cline-any cline-neutral"> </span>
|
|
220
|
+
<span class="cline-any cline-neutral"> </span>
|
|
221
|
+
<span class="cline-any cline-yes">3x</span>
|
|
222
|
+
<span class="cline-any cline-yes">4x</span>
|
|
223
|
+
<span class="cline-any cline-neutral"> </span>
|
|
224
|
+
<span class="cline-any cline-neutral"> </span>
|
|
225
|
+
<span class="cline-any cline-neutral"> </span>
|
|
226
|
+
<span class="cline-any cline-neutral"> </span>
|
|
227
|
+
<span class="cline-any cline-yes">1x</span>
|
|
228
|
+
<span class="cline-any cline-yes">1x</span>
|
|
229
|
+
<span class="cline-any cline-neutral"> </span>
|
|
230
|
+
<span class="cline-any cline-neutral"> </span>
|
|
231
|
+
<span class="cline-any cline-neutral"> </span>
|
|
232
|
+
<span class="cline-any cline-neutral"> </span>
|
|
233
|
+
<span class="cline-any cline-yes">1x</span>
|
|
234
|
+
<span class="cline-any cline-yes">1x</span>
|
|
235
|
+
<span class="cline-any cline-neutral"> </span>
|
|
236
|
+
<span class="cline-any cline-neutral"> </span>
|
|
237
|
+
<span class="cline-any cline-neutral"> </span>
|
|
238
|
+
<span class="cline-any cline-neutral"> </span>
|
|
239
|
+
<span class="cline-any cline-yes">1x</span>
|
|
240
|
+
<span class="cline-any cline-yes">1x</span>
|
|
241
|
+
<span class="cline-any cline-neutral"> </span>
|
|
242
|
+
<span class="cline-any cline-neutral"> </span>
|
|
243
|
+
<span class="cline-any cline-neutral"> </span>
|
|
244
|
+
<span class="cline-any cline-neutral"> </span>
|
|
245
|
+
<span class="cline-any cline-yes">1x</span>
|
|
118
246
|
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
|
119
247
|
* Start a high resolution timer
|
|
120
248
|
*/
|
|
@@ -141,6 +269,70 @@ export interface LoggerAdapter {
|
|
|
141
269
|
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
142
270
|
write(chunk: Record<string, any>): boolean;
|
|
143
271
|
}
|
|
272
|
+
|
|
273
|
+
export type MetricAttributes = Record<string, string | number | boolean>;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Common interface for metric tracking backends (Prometheus, Sentry, etc.).
|
|
277
|
+
* Inspired by Node.js diagnostics channels — adapters subscribe to a shared
|
|
278
|
+
* metrics channel and receive all published measurements.
|
|
279
|
+
*/
|
|
280
|
+
export interface MetricsAdapter {
|
|
281
|
+
/** Increment a monotonic counter */
|
|
282
|
+
increment(name: string, value?: number, attributes?: MetricAttributes): void;
|
|
283
|
+
/** Record the current value of a gauge */
|
|
284
|
+
gauge(name: string, value: number, attributes?: MetricAttributes): void;
|
|
285
|
+
/** Record a sample in a distribution/histogram */
|
|
286
|
+
distribution(name: string, value: number, attributes?: MetricAttributes): void;
|
|
287
|
+
/** Record a timing in milliseconds */
|
|
288
|
+
timing(name: string, value: number, attributes?: MetricAttributes): void;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* A publish/subscribe metrics channel. Adapters register via `subscribe` and
|
|
293
|
+
* receive all subsequent measurements. Unsubscribe by calling the returned
|
|
294
|
+
* dispose function — the same pattern as `diagnostics_channel.subscribe`.
|
|
295
|
+
*/
|
|
296
|
+
export class Metrics implements MetricsAdapter {
|
|
297
|
+
readonly #adapters = new Set<MetricsAdapter>();
|
|
298
|
+
|
|
299
|
+
subscribe(adapter: MetricsAdapter): () => void {
|
|
300
|
+
this.#adapters.add(adapter);
|
|
301
|
+
return () => {
|
|
302
|
+
this.#adapters.delete(adapter);
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
hasSubscribers(): boolean {
|
|
307
|
+
return this.#adapters.size > 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
increment(name: string, value = 1, attributes?: MetricAttributes): void {
|
|
311
|
+
for (const adapter of this.#adapters) {
|
|
312
|
+
adapter.increment(name, value, attributes);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
gauge(name: string, value: number, attributes?: MetricAttributes): void {
|
|
317
|
+
for (const adapter of this.#adapters) {
|
|
318
|
+
adapter.gauge(name, value, attributes);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
distribution(name: string, value: number, attributes?: MetricAttributes): void {
|
|
323
|
+
for (const adapter of this.#adapters) {
|
|
324
|
+
adapter.distribution(name, value, attributes);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
timing(name: string, value: number, attributes?: MetricAttributes): void {
|
|
329
|
+
for (const adapter of this.#adapters) {
|
|
330
|
+
adapter.timing(name, value, attributes);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export const metrics = new Metrics();
|
|
144
336
|
</pre></td></tr></table></pre>
|
|
145
337
|
|
|
146
338
|
<div class='push'></div><!-- for sticky footer -->
|
|
@@ -148,7 +340,7 @@ export interface LoggerAdapter {
|
|
|
148
340
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
149
341
|
Code coverage generated by
|
|
150
342
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
151
|
-
at 2026-04-
|
|
343
|
+
at 2026-04-16T16:01:23.455Z
|
|
152
344
|
</div>
|
|
153
345
|
<script src="prettify.js"></script>
|
|
154
346
|
<script>
|
package/dist/index.d.ts
CHANGED
|
@@ -15,3 +15,34 @@ export interface LoggerAdapter {
|
|
|
15
15
|
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
16
16
|
write(chunk: Record<string, any>): boolean;
|
|
17
17
|
}
|
|
18
|
+
export type MetricAttributes = Record<string, string | number | boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Common interface for metric tracking backends (Prometheus, Sentry, etc.).
|
|
21
|
+
* Inspired by Node.js diagnostics channels — adapters subscribe to a shared
|
|
22
|
+
* metrics channel and receive all published measurements.
|
|
23
|
+
*/
|
|
24
|
+
export interface MetricsAdapter {
|
|
25
|
+
/** Increment a monotonic counter */
|
|
26
|
+
increment(name: string, value?: number, attributes?: MetricAttributes): void;
|
|
27
|
+
/** Record the current value of a gauge */
|
|
28
|
+
gauge(name: string, value: number, attributes?: MetricAttributes): void;
|
|
29
|
+
/** Record a sample in a distribution/histogram */
|
|
30
|
+
distribution(name: string, value: number, attributes?: MetricAttributes): void;
|
|
31
|
+
/** Record a timing in milliseconds */
|
|
32
|
+
timing(name: string, value: number, attributes?: MetricAttributes): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A publish/subscribe metrics channel. Adapters register via `subscribe` and
|
|
36
|
+
* receive all subsequent measurements. Unsubscribe by calling the returned
|
|
37
|
+
* dispose function — the same pattern as `diagnostics_channel.subscribe`.
|
|
38
|
+
*/
|
|
39
|
+
export declare class Metrics implements MetricsAdapter {
|
|
40
|
+
#private;
|
|
41
|
+
subscribe(adapter: MetricsAdapter): () => void;
|
|
42
|
+
hasSubscribers(): boolean;
|
|
43
|
+
increment(name: string, value?: number, attributes?: MetricAttributes): void;
|
|
44
|
+
gauge(name: string, value: number, attributes?: MetricAttributes): void;
|
|
45
|
+
distribution(name: string, value: number, attributes?: MetricAttributes): void;
|
|
46
|
+
timing(name: string, value: number, attributes?: MetricAttributes): void;
|
|
47
|
+
}
|
|
48
|
+
export declare const metrics: Metrics;
|
package/dist/index.js
CHANGED
|
@@ -19,3 +19,41 @@ export function startTimer() {
|
|
|
19
19
|
},
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* A publish/subscribe metrics channel. Adapters register via `subscribe` and
|
|
24
|
+
* receive all subsequent measurements. Unsubscribe by calling the returned
|
|
25
|
+
* dispose function — the same pattern as `diagnostics_channel.subscribe`.
|
|
26
|
+
*/
|
|
27
|
+
export class Metrics {
|
|
28
|
+
#adapters = new Set();
|
|
29
|
+
subscribe(adapter) {
|
|
30
|
+
this.#adapters.add(adapter);
|
|
31
|
+
return () => {
|
|
32
|
+
this.#adapters.delete(adapter);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
hasSubscribers() {
|
|
36
|
+
return this.#adapters.size > 0;
|
|
37
|
+
}
|
|
38
|
+
increment(name, value = 1, attributes) {
|
|
39
|
+
for (const adapter of this.#adapters) {
|
|
40
|
+
adapter.increment(name, value, attributes);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
gauge(name, value, attributes) {
|
|
44
|
+
for (const adapter of this.#adapters) {
|
|
45
|
+
adapter.gauge(name, value, attributes);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
distribution(name, value, attributes) {
|
|
49
|
+
for (const adapter of this.#adapters) {
|
|
50
|
+
adapter.distribution(name, value, attributes);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
timing(name, value, attributes) {
|
|
54
|
+
for (const adapter of this.#adapters) {
|
|
55
|
+
adapter.timing(name, value, attributes);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export const metrics = new Metrics();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/observability",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"clean": "rm -rf dist",
|
|
17
|
-
"lint": "
|
|
17
|
+
"lint": "oxlint .",
|
|
18
|
+
"format": "oxfmt .",
|
|
18
19
|
"test": "vitest",
|
|
19
20
|
"test:coverage": "pnpm run test --coverage",
|
|
20
21
|
"build": "pnpm run clean && tsgo",
|
|
21
22
|
"prepublishOnly": "pnpm run clean && pnpm run build"
|
|
22
23
|
},
|
|
23
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "2c4100d80823bcf16bd962b4b8a26bd078522dfc"
|
|
24
25
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { startTimer } from '../index.js';
|
|
1
|
+
import { Metrics, metrics, startTimer } from '../index.js';
|
|
2
2
|
|
|
3
3
|
test('startTimer', async () => {
|
|
4
4
|
const timer = startTimer();
|
|
@@ -15,3 +15,118 @@ test('startTimer', async () => {
|
|
|
15
15
|
duration = timer.getDuration();
|
|
16
16
|
expect(duration).toBeLessThan(0.01);
|
|
17
17
|
});
|
|
18
|
+
|
|
19
|
+
test('Metrics: hasSubscribers returns false with no adapters', () => {
|
|
20
|
+
const m = new Metrics();
|
|
21
|
+
expect(m.hasSubscribers()).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('Metrics: subscribe adds adapter and returns unsubscribe function', () => {
|
|
25
|
+
const m = new Metrics();
|
|
26
|
+
const adapter = {
|
|
27
|
+
increment: vi.fn(),
|
|
28
|
+
gauge: vi.fn(),
|
|
29
|
+
distribution: vi.fn(),
|
|
30
|
+
timing: vi.fn(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const unsubscribe = m.subscribe(adapter);
|
|
34
|
+
expect(m.hasSubscribers()).toBe(true);
|
|
35
|
+
|
|
36
|
+
unsubscribe();
|
|
37
|
+
expect(m.hasSubscribers()).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('Metrics: routes increment to all adapters with default value', () => {
|
|
41
|
+
const m = new Metrics();
|
|
42
|
+
const a = {
|
|
43
|
+
increment: vi.fn(),
|
|
44
|
+
gauge: vi.fn(),
|
|
45
|
+
distribution: vi.fn(),
|
|
46
|
+
timing: vi.fn(),
|
|
47
|
+
};
|
|
48
|
+
const b = {
|
|
49
|
+
increment: vi.fn(),
|
|
50
|
+
gauge: vi.fn(),
|
|
51
|
+
distribution: vi.fn(),
|
|
52
|
+
timing: vi.fn(),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
m.subscribe(a);
|
|
56
|
+
m.subscribe(b);
|
|
57
|
+
|
|
58
|
+
m.increment('req');
|
|
59
|
+
expect(a.increment).toHaveBeenCalledWith('req', 1, undefined);
|
|
60
|
+
expect(b.increment).toHaveBeenCalledWith('req', 1, undefined);
|
|
61
|
+
|
|
62
|
+
m.increment('req', 5, { status: '200' });
|
|
63
|
+
expect(a.increment).toHaveBeenCalledWith('req', 5, { status: '200' });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('Metrics: routes gauge to all adapters', () => {
|
|
67
|
+
const m = new Metrics();
|
|
68
|
+
const adapter = {
|
|
69
|
+
increment: vi.fn(),
|
|
70
|
+
gauge: vi.fn(),
|
|
71
|
+
distribution: vi.fn(),
|
|
72
|
+
timing: vi.fn(),
|
|
73
|
+
};
|
|
74
|
+
m.subscribe(adapter);
|
|
75
|
+
|
|
76
|
+
m.gauge('cpu', 0.75, { host: 'web-1' });
|
|
77
|
+
expect(adapter.gauge).toHaveBeenCalledWith('cpu', 0.75, { host: 'web-1' });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('Metrics: routes distribution to all adapters', () => {
|
|
81
|
+
const m = new Metrics();
|
|
82
|
+
const adapter = {
|
|
83
|
+
increment: vi.fn(),
|
|
84
|
+
gauge: vi.fn(),
|
|
85
|
+
distribution: vi.fn(),
|
|
86
|
+
timing: vi.fn(),
|
|
87
|
+
};
|
|
88
|
+
m.subscribe(adapter);
|
|
89
|
+
|
|
90
|
+
m.distribution('payload.size', 1024);
|
|
91
|
+
expect(adapter.distribution).toHaveBeenCalledWith(
|
|
92
|
+
'payload.size',
|
|
93
|
+
1024,
|
|
94
|
+
undefined,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('Metrics: routes timing to all adapters', () => {
|
|
99
|
+
const m = new Metrics();
|
|
100
|
+
const adapter = {
|
|
101
|
+
increment: vi.fn(),
|
|
102
|
+
gauge: vi.fn(),
|
|
103
|
+
distribution: vi.fn(),
|
|
104
|
+
timing: vi.fn(),
|
|
105
|
+
};
|
|
106
|
+
m.subscribe(adapter);
|
|
107
|
+
|
|
108
|
+
m.timing('db.query', 42, { table: 'users' });
|
|
109
|
+
expect(adapter.timing).toHaveBeenCalledWith('db.query', 42, {
|
|
110
|
+
table: 'users',
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('Metrics: does not route to unsubscribed adapters', () => {
|
|
115
|
+
const m = new Metrics();
|
|
116
|
+
const adapter = {
|
|
117
|
+
increment: vi.fn(),
|
|
118
|
+
gauge: vi.fn(),
|
|
119
|
+
distribution: vi.fn(),
|
|
120
|
+
timing: vi.fn(),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const unsubscribe = m.subscribe(adapter);
|
|
124
|
+
unsubscribe();
|
|
125
|
+
|
|
126
|
+
m.increment('req');
|
|
127
|
+
expect(adapter.increment).not.toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('metrics is a shared Metrics instance', () => {
|
|
131
|
+
expect(metrics).toBeInstanceOf(Metrics);
|
|
132
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -24,3 +24,75 @@ export interface LoggerAdapter {
|
|
|
24
24
|
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
25
25
|
write(chunk: Record<string, any>): boolean;
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
export type MetricAttributes = Record<string, string | number | boolean>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Common interface for metric tracking backends (Prometheus, Sentry, etc.).
|
|
32
|
+
* Inspired by Node.js diagnostics channels — adapters subscribe to a shared
|
|
33
|
+
* metrics channel and receive all published measurements.
|
|
34
|
+
*/
|
|
35
|
+
export interface MetricsAdapter {
|
|
36
|
+
/** Increment a monotonic counter */
|
|
37
|
+
increment(name: string, value?: number, attributes?: MetricAttributes): void;
|
|
38
|
+
/** Record the current value of a gauge */
|
|
39
|
+
gauge(name: string, value: number, attributes?: MetricAttributes): void;
|
|
40
|
+
/** Record a sample in a distribution/histogram */
|
|
41
|
+
distribution(
|
|
42
|
+
name: string,
|
|
43
|
+
value: number,
|
|
44
|
+
attributes?: MetricAttributes,
|
|
45
|
+
): void;
|
|
46
|
+
/** Record a timing in milliseconds */
|
|
47
|
+
timing(name: string, value: number, attributes?: MetricAttributes): void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A publish/subscribe metrics channel. Adapters register via `subscribe` and
|
|
52
|
+
* receive all subsequent measurements. Unsubscribe by calling the returned
|
|
53
|
+
* dispose function — the same pattern as `diagnostics_channel.subscribe`.
|
|
54
|
+
*/
|
|
55
|
+
export class Metrics implements MetricsAdapter {
|
|
56
|
+
readonly #adapters = new Set<MetricsAdapter>();
|
|
57
|
+
|
|
58
|
+
subscribe(adapter: MetricsAdapter): () => void {
|
|
59
|
+
this.#adapters.add(adapter);
|
|
60
|
+
return () => {
|
|
61
|
+
this.#adapters.delete(adapter);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
hasSubscribers(): boolean {
|
|
66
|
+
return this.#adapters.size > 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
increment(name: string, value = 1, attributes?: MetricAttributes): void {
|
|
70
|
+
for (const adapter of this.#adapters) {
|
|
71
|
+
adapter.increment(name, value, attributes);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
gauge(name: string, value: number, attributes?: MetricAttributes): void {
|
|
76
|
+
for (const adapter of this.#adapters) {
|
|
77
|
+
adapter.gauge(name, value, attributes);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
distribution(
|
|
82
|
+
name: string,
|
|
83
|
+
value: number,
|
|
84
|
+
attributes?: MetricAttributes,
|
|
85
|
+
): void {
|
|
86
|
+
for (const adapter of this.#adapters) {
|
|
87
|
+
adapter.distribution(name, value, attributes);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
timing(name: string, value: number, attributes?: MetricAttributes): void {
|
|
92
|
+
for (const adapter of this.#adapters) {
|
|
93
|
+
adapter.timing(name, value, attributes);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const metrics = new Metrics();
|