@devmoods/observability 0.1.4 → 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 +213 -6
- package/dist/index.d.ts +35 -0
- package/dist/index.js +38 -0
- package/package.json +4 -3
- package/src/__tests__/index.test.ts +116 -1
- package/src/index.ts +77 -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
|
|
|
@@ -84,7 +84,76 @@
|
|
|
84
84
|
<a name='L19'></a><a href='#L19'>19</a>
|
|
85
85
|
<a name='L20'></a><a href='#L20'>20</a>
|
|
86
86
|
<a name='L21'></a><a href='#L21'>21</a>
|
|
87
|
-
<a name='L22'></a><a href='#L22'>22</a
|
|
87
|
+
<a name='L22'></a><a href='#L22'>22</a>
|
|
88
|
+
<a name='L23'></a><a href='#L23'>23</a>
|
|
89
|
+
<a name='L24'></a><a href='#L24'>24</a>
|
|
90
|
+
<a name='L25'></a><a href='#L25'>25</a>
|
|
91
|
+
<a name='L26'></a><a href='#L26'>26</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>
|
|
88
157
|
<span class="cline-any cline-neutral"> </span>
|
|
89
158
|
<span class="cline-any cline-neutral"> </span>
|
|
90
159
|
<span class="cline-any cline-neutral"> </span>
|
|
@@ -105,6 +174,75 @@
|
|
|
105
174
|
<span class="cline-any cline-neutral"> </span>
|
|
106
175
|
<span class="cline-any cline-neutral"> </span>
|
|
107
176
|
<span class="cline-any cline-neutral"> </span>
|
|
177
|
+
<span class="cline-any cline-neutral"> </span>
|
|
178
|
+
<span class="cline-any cline-neutral"> </span>
|
|
179
|
+
<span class="cline-any cline-neutral"> </span>
|
|
180
|
+
<span class="cline-any cline-neutral"> </span>
|
|
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>
|
|
108
246
|
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
|
109
247
|
* Start a high resolution timer
|
|
110
248
|
*/
|
|
@@ -126,6 +264,75 @@ export function startTimer() {
|
|
|
126
264
|
},
|
|
127
265
|
};
|
|
128
266
|
}
|
|
267
|
+
|
|
268
|
+
export interface LoggerAdapter {
|
|
269
|
+
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
270
|
+
write(chunk: Record<string, any>): boolean;
|
|
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();
|
|
129
336
|
</pre></td></tr></table></pre>
|
|
130
337
|
|
|
131
338
|
<div class='push'></div><!-- for sticky footer -->
|
|
@@ -133,7 +340,7 @@ export function startTimer() {
|
|
|
133
340
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
134
341
|
Code coverage generated by
|
|
135
342
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
136
|
-
at 2026-04-
|
|
343
|
+
at 2026-04-16T16:01:23.455Z
|
|
137
344
|
</div>
|
|
138
345
|
<script src="prettify.js"></script>
|
|
139
346
|
<script>
|
package/dist/index.d.ts
CHANGED
|
@@ -11,3 +11,38 @@ export declare function startTimer(): {
|
|
|
11
11
|
*/
|
|
12
12
|
getDuration(): number;
|
|
13
13
|
};
|
|
14
|
+
export interface LoggerAdapter {
|
|
15
|
+
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
16
|
+
write(chunk: Record<string, any>): boolean;
|
|
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
|
@@ -19,3 +19,80 @@ export function startTimer() {
|
|
|
19
19
|
},
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
export interface LoggerAdapter {
|
|
24
|
+
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
25
|
+
write(chunk: Record<string, any>): boolean;
|
|
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();
|