fhirsmith 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/CHANGELOG.md +42 -0
- package/FHIRsmith.png +0 -0
- package/README.md +277 -0
- package/config-template.json +144 -0
- package/library/folder-setup.js +58 -0
- package/library/html-server.js +166 -0
- package/library/html.js +835 -0
- package/library/i18nsupport.js +259 -0
- package/library/languages.js +779 -0
- package/library/logger-telnet.js +205 -0
- package/library/logger.js +279 -0
- package/library/package-manager.js +876 -0
- package/library/utilities.js +196 -0
- package/library/version-utilities.js +1056 -0
- package/npmprojector/config-example.json +13 -0
- package/npmprojector/indexer.js +394 -0
- package/npmprojector/npmprojector.js +395 -0
- package/npmprojector/readme.md +174 -0
- package/npmprojector/watcher.js +335 -0
- package/package.json +119 -0
- package/packages/package-crawler.js +846 -0
- package/packages/packages-template.html +126 -0
- package/packages/packages.js +2838 -0
- package/passwords.ini +2 -0
- package/publisher/publisher-template.html +208 -0
- package/publisher/publisher.js +2167 -0
- package/publisher/task-draft.js +458 -0
- package/registry/api.js +735 -0
- package/registry/crawler.js +637 -0
- package/registry/model.js +513 -0
- package/registry/readme.md +243 -0
- package/registry/registry-data.json +121015 -0
- package/registry/registry-template.html +126 -0
- package/registry/registry.js +1395 -0
- package/registry/test-runner.js +237 -0
- package/root-template.html +124 -0
- package/server.js +524 -0
- package/shl/private-key.pem +5 -0
- package/shl/public-key.pem +18 -0
- package/shl/shl.js +1125 -0
- package/shl/vhl.js +69 -0
- package/static/FHIRsmith128.png +0 -0
- package/static/FHIRsmith16.png +0 -0
- package/static/FHIRsmith32.png +0 -0
- package/static/FHIRsmith64.png +0 -0
- package/static/assets/css/bootstrap-fhir.css +5302 -0
- package/static/assets/css/bootstrap-glyphicons.css +2 -0
- package/static/assets/css/bootstrap.css +4097 -0
- package/static/assets/css/jquery-ui.css +523 -0
- package/static/assets/css/jquery-ui.structure.css +863 -0
- package/static/assets/css/jquery-ui.structure.min.css +5 -0
- package/static/assets/css/jquery-ui.theme.css +439 -0
- package/static/assets/css/jquery-ui.theme.min.css +5 -0
- package/static/assets/css/jquery.ui.all.css +7 -0
- package/static/assets/css/modules.css +18 -0
- package/static/assets/css/project.css +367 -0
- package/static/assets/css/pygments-manni.css +66 -0
- package/static/assets/css/tags.css +74 -0
- package/static/assets/css/xml.css +2 -0
- package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
- package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
- package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
- package/static/assets/ico/favicon.ico +0 -0
- package/static/assets/ico/favicon.png +0 -0
- package/static/assets/images/fhir-logo-www.png +0 -0
- package/static/assets/images/fhir-logo.png +0 -0
- package/static/assets/images/hl7-logo.png +0 -0
- package/static/assets/images/logo_ansinew.jpg +0 -0
- package/static/assets/images/search.png +0 -0
- package/static/assets/images/stripe.png +0 -0
- package/static/assets/images/target.png +0 -0
- package/static/assets/images/tx-registry-root.gif +0 -0
- package/static/assets/images/tx-registry.png +0 -0
- package/static/assets/images/tx-server.png +0 -0
- package/static/assets/images/tx-version.png +0 -0
- package/static/assets/js/bootstrap.min.js +6 -0
- package/static/assets/js/fhir-gw.js +259 -0
- package/static/assets/js/fhir.js +2 -0
- package/static/assets/js/html5shiv.js +8 -0
- package/static/assets/js/jcookie.js +96 -0
- package/static/assets/js/jquery-ui.min.js +6 -0
- package/static/assets/js/jquery.js +10716 -0
- package/static/assets/js/jquery.min.js +2 -0
- package/static/assets/js/jquery.ui.core.js +314 -0
- package/static/assets/js/jquery.ui.draggable.js +825 -0
- package/static/assets/js/jquery.ui.mouse.js +162 -0
- package/static/assets/js/jquery.ui.resizable.js +842 -0
- package/static/assets/js/jquery.ui.widget.js +268 -0
- package/static/assets/js/json2.js +487 -0
- package/static/assets/js/jtip.js +97 -0
- package/static/assets/js/respond.min.js +6 -0
- package/static/assets/js/statuspage.js +70 -0
- package/static/assets/js/xml.js +2 -0
- package/static/dist/js/bootstrap.js +1964 -0
- package/static/favicon.png +0 -0
- package/static/fhir.css +626 -0
- package/static/icon-fhir-16.png +0 -0
- package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
- package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- package/static/images/ui-icons_222222_256x240.png +0 -0
- package/static/images/ui-icons_228ef1_256x240.png +0 -0
- package/static/images/ui-icons_ef8c08_256x240.png +0 -0
- package/static/images/ui-icons_ffd27a_256x240.png +0 -0
- package/static/images/ui-icons_ffffff_256x240.png +0 -0
- package/static/js/jquery.effects.blind.js +49 -0
- package/static/js/jquery.effects.bounce.js +78 -0
- package/static/js/jquery.effects.clip.js +54 -0
- package/static/js/jquery.effects.core.js +763 -0
- package/static/js/jquery.effects.drop.js +50 -0
- package/static/js/jquery.effects.explode.js +79 -0
- package/static/js/jquery.effects.fade.js +32 -0
- package/static/js/jquery.effects.fold.js +56 -0
- package/static/js/jquery.effects.highlight.js +50 -0
- package/static/js/jquery.effects.pulsate.js +51 -0
- package/static/js/jquery.effects.scale.js +178 -0
- package/static/js/jquery.effects.shake.js +57 -0
- package/static/js/jquery.effects.slide.js +50 -0
- package/static/js/jquery.effects.transfer.js +45 -0
- package/static/js/jquery.ui.accordion.js +611 -0
- package/static/js/jquery.ui.autocomplete.js +612 -0
- package/static/js/jquery.ui.button.js +416 -0
- package/static/js/jquery.ui.datepicker.js +1823 -0
- package/static/js/jquery.ui.dialog.js +878 -0
- package/static/js/jquery.ui.droppable.js +296 -0
- package/static/js/jquery.ui.position.js +252 -0
- package/static/js/jquery.ui.progressbar.js +109 -0
- package/static/js/jquery.ui.selectable.js +266 -0
- package/static/js/jquery.ui.slider.js +666 -0
- package/static/js/jquery.ui.sortable.js +1077 -0
- package/static/js/jquery.ui.tabs.js +758 -0
- package/stats.js +80 -0
- package/test-cache/vsac/vsac-valuesets.db +0 -0
- package/token/nginx_passport_setup.md +383 -0
- package/token/security_guide.md +294 -0
- package/token/token-template.html +330 -0
- package/token/token.js +1300 -0
- package/translations/Messages.properties +1510 -0
- package/translations/Messages_ar.properties +1399 -0
- package/translations/Messages_de.properties +836 -0
- package/translations/Messages_es.properties +737 -0
- package/translations/Messages_fr.properties +1 -0
- package/translations/Messages_ja.properties +893 -0
- package/translations/Messages_nl.properties +1357 -0
- package/translations/Messages_pt.properties +1302 -0
- package/translations/Messages_ru.properties +1 -0
- package/translations/Messages_uz.properties +1 -0
- package/translations/Messages_zh.properties +1 -0
- package/translations/rendering-phrases.properties +1128 -0
- package/translations/rendering-phrases_ar.properties +1091 -0
- package/translations/rendering-phrases_de.properties +6 -0
- package/translations/rendering-phrases_es.properties +6 -0
- package/translations/rendering-phrases_fr.properties +624 -0
- package/translations/rendering-phrases_ja.properties +21 -0
- package/translations/rendering-phrases_nl.properties +970 -0
- package/translations/rendering-phrases_pt.properties +1020 -0
- package/translations/rendering-phrases_ru.properties +1094 -0
- package/translations/rendering-phrases_uz.properties +1 -0
- package/translations/rendering-phrases_zh.properties +1 -0
- package/tx/README.md +418 -0
- package/tx/cm/cm-api.js +110 -0
- package/tx/cm/cm-database.js +735 -0
- package/tx/cm/cm-package.js +325 -0
- package/tx/cs/cs-api.js +789 -0
- package/tx/cs/cs-areacode.js +615 -0
- package/tx/cs/cs-country.js +1110 -0
- package/tx/cs/cs-cpt.js +785 -0
- package/tx/cs/cs-cs.js +1579 -0
- package/tx/cs/cs-currency.js +539 -0
- package/tx/cs/cs-db.js +1321 -0
- package/tx/cs/cs-hgvs.js +329 -0
- package/tx/cs/cs-lang.js +465 -0
- package/tx/cs/cs-loinc.js +1485 -0
- package/tx/cs/cs-mimetypes.js +238 -0
- package/tx/cs/cs-ndc.js +704 -0
- package/tx/cs/cs-omop.js +1025 -0
- package/tx/cs/cs-provider-api.js +43 -0
- package/tx/cs/cs-provider-list.js +37 -0
- package/tx/cs/cs-rxnorm.js +808 -0
- package/tx/cs/cs-snomed.js +1102 -0
- package/tx/cs/cs-ucum.js +514 -0
- package/tx/cs/cs-unii.js +271 -0
- package/tx/cs/cs-uri.js +218 -0
- package/tx/cs/cs-usstates.js +305 -0
- package/tx/dev.fhir.org.yml +14 -0
- package/tx/fixtures/test-cases-setup.json +18 -0
- package/tx/fixtures/test-cases.yml +16 -0
- package/tx/html/codesystem-operations.liquid +25 -0
- package/tx/html/home-metrics.liquid +247 -0
- package/tx/html/operations-form.liquid +148 -0
- package/tx/html/search-form.liquid +62 -0
- package/tx/html/tx-template.html +133 -0
- package/tx/html/valueset-operations.liquid +54 -0
- package/tx/importers/atc-to-fhir.js +316 -0
- package/tx/importers/import-loinc.module.js +1536 -0
- package/tx/importers/import-ndc.module.js +1088 -0
- package/tx/importers/import-rxnorm.module.js +898 -0
- package/tx/importers/import-sct.module.js +2457 -0
- package/tx/importers/import-unii.module.js +601 -0
- package/tx/importers/readme.md +453 -0
- package/tx/importers/subset-loinc.module.js +1081 -0
- package/tx/importers/subset-rxnorm.module.js +938 -0
- package/tx/importers/tx-import-base.js +351 -0
- package/tx/importers/tx-import-settings.js +310 -0
- package/tx/importers/tx-import.js +357 -0
- package/tx/library/canonical-resource.js +88 -0
- package/tx/library/capabilitystatement.js +292 -0
- package/tx/library/codesystem.js +774 -0
- package/tx/library/conceptmap.js +568 -0
- package/tx/library/designations.js +932 -0
- package/tx/library/errors.js +77 -0
- package/tx/library/extensions.js +117 -0
- package/tx/library/namingsystem.js +322 -0
- package/tx/library/operation-outcome.js +127 -0
- package/tx/library/parameters.js +105 -0
- package/tx/library/renderer.js +1559 -0
- package/tx/library/terminologycapabilities.js +418 -0
- package/tx/library/ucum-parsers.js +1029 -0
- package/tx/library/ucum-service.js +370 -0
- package/tx/library/ucum-types.js +1099 -0
- package/tx/library/valueset.js +543 -0
- package/tx/library.js +676 -0
- package/tx/ocl/cm-ocl.js +106 -0
- package/tx/ocl/cs-ocl.js +39 -0
- package/tx/ocl/vs-ocl.js +105 -0
- package/tx/operation-context.js +568 -0
- package/tx/params.js +613 -0
- package/tx/provider.js +403 -0
- package/tx/sct/ecl.js +1560 -0
- package/tx/sct/expressions.js +2077 -0
- package/tx/sct/structures.js +1396 -0
- package/tx/tx-html.js +1063 -0
- package/tx/tx.fhir.org.yml +39 -0
- package/tx/tx.js +927 -0
- package/tx/vs/vs-api.js +112 -0
- package/tx/vs/vs-database.js +786 -0
- package/tx/vs/vs-package.js +358 -0
- package/tx/vs/vs-vsac.js +366 -0
- package/tx/workers/batch-validate.js +129 -0
- package/tx/workers/batch.js +361 -0
- package/tx/workers/closure.js +32 -0
- package/tx/workers/expand.js +1845 -0
- package/tx/workers/lookup.js +407 -0
- package/tx/workers/metadata.js +467 -0
- package/tx/workers/operations.js +34 -0
- package/tx/workers/read.js +164 -0
- package/tx/workers/search.js +384 -0
- package/tx/workers/subsumes.js +334 -0
- package/tx/workers/translate.js +492 -0
- package/tx/workers/validate.js +2504 -0
- package/tx/workers/worker.js +904 -0
- package/tx/xml/capabilitystatement-xml.js +63 -0
- package/tx/xml/codesystem-xml.js +62 -0
- package/tx/xml/conceptmap-xml.js +65 -0
- package/tx/xml/namingsystem-xml.js +65 -0
- package/tx/xml/operationoutcome-xml.js +127 -0
- package/tx/xml/parameters-xml.js +312 -0
- package/tx/xml/terminologycapabilities-xml.js +64 -0
- package/tx/xml/valueset-xml.js +64 -0
- package/tx/xml/xml-base.js +603 -0
- package/vcl/vcl-parser.js +1098 -0
- package/vcl/vcl.js +253 -0
- package/windows-install.js +19 -0
- package/xig/xig-template.html +124 -0
- package/xig/xig.js +3049 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Winston transport for streaming logs over a TCP socket
|
|
3
|
+
* This allows viewing logs remotely via telnet or a custom client
|
|
4
|
+
*/
|
|
5
|
+
const winston = require('winston');
|
|
6
|
+
const net = require('net');
|
|
7
|
+
const Transport = winston.Transport;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Winston transport that streams logs to connected TCP clients
|
|
11
|
+
* @extends {winston.Transport}
|
|
12
|
+
*/
|
|
13
|
+
class SocketTransport extends Transport {
|
|
14
|
+
/**
|
|
15
|
+
* Create a new socket transport
|
|
16
|
+
* @param {Object} options - Transport options
|
|
17
|
+
* @param {string} [options.host='127.0.0.1'] - Host to bind to
|
|
18
|
+
* @param {number} [options.port=9300] - Port to listen on
|
|
19
|
+
* @param {string} [options.level='info'] - Minimum log level to stream
|
|
20
|
+
* @param {Function} [options.format] - Custom formatter function (msg, level, meta) => string
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
super(options);
|
|
24
|
+
this.name = 'socket';
|
|
25
|
+
this.level = options.level || 'info';
|
|
26
|
+
this.clients = new Set();
|
|
27
|
+
this.format = options.format || this._defaultFormat;
|
|
28
|
+
|
|
29
|
+
// Create TCP server
|
|
30
|
+
this.server = net.createServer((socket) => {
|
|
31
|
+
console.log(`Client connected to log stream from ${socket.remoteAddress}`);
|
|
32
|
+
|
|
33
|
+
// Send welcome message
|
|
34
|
+
socket.write(`=== Connected to log stream (${new Date().toISOString()}) ===\n`);
|
|
35
|
+
socket.write(`=== Log level: ${this.level} ===\n\n`);
|
|
36
|
+
|
|
37
|
+
// Add to clients set
|
|
38
|
+
this.clients.add(socket);
|
|
39
|
+
|
|
40
|
+
socket.on('close', () => {
|
|
41
|
+
console.log(`Client disconnected from log stream: ${socket.remoteAddress}`);
|
|
42
|
+
this.clients.delete(socket);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
socket.on('error', (err) => {
|
|
46
|
+
console.error(`Socket error: ${err.message}`);
|
|
47
|
+
this.clients.delete(socket);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Support for simple commands
|
|
51
|
+
socket.on('data', (data) => {
|
|
52
|
+
const command = data.toString().trim().toLowerCase();
|
|
53
|
+
|
|
54
|
+
if (command === 'help') {
|
|
55
|
+
socket.write(this._getHelpText());
|
|
56
|
+
} else if (command === 'stats') {
|
|
57
|
+
socket.write(this._getStatsText());
|
|
58
|
+
} else if (command === 'quit' || command === 'exit') {
|
|
59
|
+
socket.end('=== Disconnected ===\n');
|
|
60
|
+
} else if (command.startsWith('level ')) {
|
|
61
|
+
const newLevel = command.split(' ')[1];
|
|
62
|
+
if (['error', 'warn', 'info', 'debug', 'verbose', 'silly'].includes(newLevel)) {
|
|
63
|
+
this.level = newLevel;
|
|
64
|
+
socket.write(`=== Log level changed to ${newLevel} ===\n`);
|
|
65
|
+
} else {
|
|
66
|
+
socket.write(`=== Invalid log level: ${newLevel} ===\n`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Start listening
|
|
73
|
+
const port = options.port || 9300;
|
|
74
|
+
const host = options.host || '127.0.0.1';
|
|
75
|
+
|
|
76
|
+
this.server.listen(port, host, () => {
|
|
77
|
+
console.log(`Log socket server running on ${host}:${port}`);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
this.server.on('error', (err) => {
|
|
81
|
+
console.error(`Socket transport error: ${err.message}`);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Winston transport method called for each log
|
|
87
|
+
* @param {Object} info - Log information
|
|
88
|
+
* @param {Function} callback - Callback function
|
|
89
|
+
*/
|
|
90
|
+
log(info, callback) {
|
|
91
|
+
setImmediate(() => {
|
|
92
|
+
this.emit('logged', info);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Skip if no clients connected
|
|
96
|
+
if (this.clients.size === 0) {
|
|
97
|
+
callback();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Format the log entry
|
|
102
|
+
const logEntry = this.format(info);
|
|
103
|
+
|
|
104
|
+
// Send to all connected clients
|
|
105
|
+
const deadClients = [];
|
|
106
|
+
for (const client of this.clients) {
|
|
107
|
+
try {
|
|
108
|
+
client.write(logEntry);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
deadClients.push(client);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Remove dead connections
|
|
115
|
+
deadClients.forEach(client => this.clients.delete(client));
|
|
116
|
+
|
|
117
|
+
callback();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Default log formatter
|
|
122
|
+
* @param {Object} info - Log information
|
|
123
|
+
* @returns {string} Formatted log entry
|
|
124
|
+
* @private
|
|
125
|
+
*/
|
|
126
|
+
_defaultFormat(info) {
|
|
127
|
+
const timestamp = info.timestamp || new Date().toISOString();
|
|
128
|
+
const level = info.level.toUpperCase().padEnd(7);
|
|
129
|
+
const message = info.message || '';
|
|
130
|
+
|
|
131
|
+
// Extract metadata excluding standard fields
|
|
132
|
+
const meta = { ...info };
|
|
133
|
+
delete meta.timestamp;
|
|
134
|
+
delete meta.level;
|
|
135
|
+
delete meta.message;
|
|
136
|
+
|
|
137
|
+
const metaStr = Object.keys(meta).length > 0
|
|
138
|
+
? ` ${JSON.stringify(meta)}`
|
|
139
|
+
: '';
|
|
140
|
+
|
|
141
|
+
return `${timestamp} [${level}] ${message}${metaStr}\n`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get help text for connected clients
|
|
146
|
+
* @returns {string} Help text
|
|
147
|
+
* @private
|
|
148
|
+
*/
|
|
149
|
+
_getHelpText() {
|
|
150
|
+
return `
|
|
151
|
+
=== Log Viewer Commands ===
|
|
152
|
+
help - Show this help
|
|
153
|
+
stats - Show connection statistics
|
|
154
|
+
level <level> - Change log level (error, warn, info, debug, verbose, silly)
|
|
155
|
+
quit/exit - Disconnect
|
|
156
|
+
|
|
157
|
+
=== Log Format ===
|
|
158
|
+
TIMESTAMP [LEVEL] MESSAGE {metadata}
|
|
159
|
+
|
|
160
|
+
=== Examples ===
|
|
161
|
+
level debug - Show debug and higher priority logs
|
|
162
|
+
level error - Show only error logs
|
|
163
|
+
|
|
164
|
+
`.trim() + '\n\n';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get statistics text for connected clients
|
|
169
|
+
* @returns {string} Statistics text
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
_getStatsText() {
|
|
173
|
+
return `
|
|
174
|
+
=== Log Statistics ===
|
|
175
|
+
Connected clients: ${this.clients.size}
|
|
176
|
+
Current log level: ${this.level}
|
|
177
|
+
Server started: ${this.server.startTime || 'unknown'}
|
|
178
|
+
Current time: ${new Date().toISOString()}
|
|
179
|
+
|
|
180
|
+
`.trim() + '\n\n';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Close the socket server
|
|
185
|
+
* @param {Function} [callback] - Callback function
|
|
186
|
+
*/
|
|
187
|
+
close(callback) {
|
|
188
|
+
// Notify all clients
|
|
189
|
+
for (const client of this.clients) {
|
|
190
|
+
try {
|
|
191
|
+
client.end('=== Log server shutting down ===\n');
|
|
192
|
+
} catch (err) {
|
|
193
|
+
// Ignore errors during shutdown
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Close server
|
|
198
|
+
this.server.close(callback);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Register the transport with Winston
|
|
203
|
+
winston.transports.Socket = SocketTransport;
|
|
204
|
+
|
|
205
|
+
module.exports = SocketTransport;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
const winston = require('winston');
|
|
2
|
+
require('winston-daily-rotate-file');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const folders = require('./folder-setup');
|
|
5
|
+
|
|
6
|
+
class Logger {
|
|
7
|
+
static _instance = null;
|
|
8
|
+
|
|
9
|
+
static getInstance(options = {}) {
|
|
10
|
+
if (!Logger._instance) {
|
|
11
|
+
Logger._instance = new Logger(options);
|
|
12
|
+
}
|
|
13
|
+
return Logger._instance;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.options = {
|
|
18
|
+
level: options.level || 'info',
|
|
19
|
+
logDir: options.logDir || folders.logsDir(),
|
|
20
|
+
console: options.console !== undefined ? options.console : true,
|
|
21
|
+
consoleErrors: options.consoleErrors !== undefined ? options.consoleErrors : false,
|
|
22
|
+
telnetErrors: options.telnetErrors !== undefined ? options.telnetErrors : false,
|
|
23
|
+
file: {
|
|
24
|
+
filename: options.file?.filename || 'server-%DATE%.log',
|
|
25
|
+
datePattern: options.file?.datePattern || 'YYYY-MM-DD',
|
|
26
|
+
maxSize: options.file?.maxSize || '20m',
|
|
27
|
+
maxFiles: options.file?.maxFiles || 14
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Ensure log directory exists
|
|
32
|
+
if (!fs.existsSync(this.options.logDir)) {
|
|
33
|
+
fs.mkdirSync(this.options.logDir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Telnet clients storage
|
|
37
|
+
this.telnetClients = new Set();
|
|
38
|
+
|
|
39
|
+
this._createLogger();
|
|
40
|
+
|
|
41
|
+
// Log logger initialization
|
|
42
|
+
this.info('Logger initialized @ ' + this.options.logDir, {
|
|
43
|
+
level: this.options.level,
|
|
44
|
+
logDir: this.options.logDir
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_createLogger() {
|
|
49
|
+
// Define formats for file output (with full metadata including stack traces)
|
|
50
|
+
const fileFormats = [
|
|
51
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
|
|
52
|
+
winston.format.errors({ stack: true }),
|
|
53
|
+
winston.format.splat(),
|
|
54
|
+
winston.format.json()
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// Create transports
|
|
58
|
+
const transports = [];
|
|
59
|
+
|
|
60
|
+
// Add file transport with rotation (includes ALL levels with full metadata)
|
|
61
|
+
const fileTransport = new winston.transports.DailyRotateFile({
|
|
62
|
+
dirname: this.options.logDir,
|
|
63
|
+
filename: this.options.file.filename,
|
|
64
|
+
datePattern: this.options.file.datePattern,
|
|
65
|
+
maxSize: this.options.file.maxSize,
|
|
66
|
+
maxFiles: this.options.file.maxFiles,
|
|
67
|
+
level: this.options.level,
|
|
68
|
+
format: winston.format.combine(...fileFormats)
|
|
69
|
+
});
|
|
70
|
+
transports.push(fileTransport);
|
|
71
|
+
|
|
72
|
+
// Add console transport if enabled
|
|
73
|
+
if (this.options.console) {
|
|
74
|
+
const consoleFormat = winston.format.combine(
|
|
75
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
|
|
76
|
+
winston.format.errors({ stack: true }),
|
|
77
|
+
winston.format.colorize({ all: true }),
|
|
78
|
+
winston.format.printf(info => {
|
|
79
|
+
const stack = info.stack ? `\n${info.stack}` : '';
|
|
80
|
+
return `${info.timestamp} ${info.level.padEnd(7)} ${info.message}${stack}`;
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const consoleTransport = new winston.transports.Console({
|
|
85
|
+
level: this.options.level,
|
|
86
|
+
format: consoleFormat
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
transports.push(consoleTransport);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Create the winston logger
|
|
93
|
+
this.logger = winston.createLogger({
|
|
94
|
+
level: this.options.level,
|
|
95
|
+
transports,
|
|
96
|
+
exitOnError: false
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Telnet client management
|
|
101
|
+
addTelnetClient(socket) {
|
|
102
|
+
this.telnetClients.add(socket);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
removeTelnetClient(socket) {
|
|
106
|
+
this.telnetClients.delete(socket);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_sendToTelnet(level, message, stack, options) {
|
|
110
|
+
// Check if we should send errors/warnings to telnet
|
|
111
|
+
if (!options.telnetErrors && (level === 'error' || level === 'warn')) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 23);
|
|
116
|
+
let line = `${timestamp} ${level.padEnd(7)} ${message}\n`;
|
|
117
|
+
if (stack) {
|
|
118
|
+
line += stack + '\n';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const client of this.telnetClients) {
|
|
122
|
+
try {
|
|
123
|
+
client.write(line);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
// Client disconnected, remove it
|
|
126
|
+
this.telnetClients.delete(client);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_shouldLogToConsole(level, options) {
|
|
132
|
+
if (level === 'error' || level === 'warn') {
|
|
133
|
+
return options.consoleErrors;
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_log(level, messageOrError, meta, options) {
|
|
139
|
+
let message;
|
|
140
|
+
let stack;
|
|
141
|
+
|
|
142
|
+
// Check if we should skip console for errors/warnings
|
|
143
|
+
const skipConsole = !this._shouldLogToConsole(level, options);
|
|
144
|
+
|
|
145
|
+
// Handle Error objects
|
|
146
|
+
if (messageOrError instanceof Error) {
|
|
147
|
+
message = messageOrError.message;
|
|
148
|
+
stack = messageOrError.stack;
|
|
149
|
+
if (skipConsole) {
|
|
150
|
+
// Log only to file transport
|
|
151
|
+
this.logger.transports
|
|
152
|
+
.filter(t => !(t instanceof winston.transports.Console))
|
|
153
|
+
.forEach(t => t.log({ level, message, stack, ...meta }));
|
|
154
|
+
} else {
|
|
155
|
+
this.logger[level](message, {stack, ...meta});
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
message = String(messageOrError);
|
|
159
|
+
stack = meta?.stack;
|
|
160
|
+
if (skipConsole) {
|
|
161
|
+
this.logger.transports
|
|
162
|
+
.filter(t => !(t instanceof winston.transports.Console))
|
|
163
|
+
.forEach(t => t.log({ level, message, ...meta }));
|
|
164
|
+
} else {
|
|
165
|
+
this.logger[level](message, meta);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this._sendToTelnet(level, message, stack, options);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
error(message, meta = {}) {
|
|
173
|
+
this._log('error', message, meta, this.options);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
warn(message, meta = {}) {
|
|
177
|
+
this._log('warn', message, meta, this.options);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
info(message, meta = {}) {
|
|
181
|
+
this._log('info', message, meta, this.options);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
debug(message, meta = {}) {
|
|
185
|
+
this._log('debug', message, meta, this.options);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
verbose(message, meta = {}) {
|
|
189
|
+
this._log('verbose', message, meta, this.options);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
log(level, message, meta = {}) {
|
|
193
|
+
this._log(level, message, meta, this.options);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
child(defaultMeta = {}) {
|
|
197
|
+
const self = this;
|
|
198
|
+
|
|
199
|
+
// Build module-specific options
|
|
200
|
+
const childOptions = {
|
|
201
|
+
consoleErrors: defaultMeta.consoleErrors ?? self.options.consoleErrors,
|
|
202
|
+
telnetErrors: defaultMeta.telnetErrors ?? self.options.telnetErrors
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Remove our custom options from defaultMeta so they don't pollute log output
|
|
206
|
+
const cleanMeta = { ...defaultMeta };
|
|
207
|
+
delete cleanMeta.consoleErrors;
|
|
208
|
+
delete cleanMeta.telnetErrors;
|
|
209
|
+
|
|
210
|
+
if (cleanMeta.module) {
|
|
211
|
+
const modulePrefix = `{${cleanMeta.module}}`;
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
error: (messageOrError, meta = {}) => {
|
|
215
|
+
if (messageOrError instanceof Error) {
|
|
216
|
+
const prefixedError = new Error(`${modulePrefix}: ${messageOrError.message}`);
|
|
217
|
+
prefixedError.stack = messageOrError.stack;
|
|
218
|
+
self._log('error', prefixedError, meta, childOptions);
|
|
219
|
+
} else {
|
|
220
|
+
self._log('error', `${modulePrefix}: ${messageOrError}`, meta, childOptions);
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
warn: (messageOrError, meta = {}) => {
|
|
224
|
+
if (messageOrError instanceof Error) {
|
|
225
|
+
const prefixedError = new Error(`${modulePrefix}: ${messageOrError.message}`);
|
|
226
|
+
prefixedError.stack = messageOrError.stack;
|
|
227
|
+
self._log('warn', prefixedError, meta, childOptions);
|
|
228
|
+
} else {
|
|
229
|
+
self._log('warn', `${modulePrefix}: ${messageOrError}`, meta, childOptions);
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
info: (message, meta = {}) => self._log('info', `${modulePrefix}: ${message}`, meta, childOptions),
|
|
233
|
+
debug: (message, meta = {}) => self._log('debug', `${modulePrefix}: ${message}`, meta, childOptions),
|
|
234
|
+
verbose: (message, meta = {}) => self._log('verbose', `${modulePrefix}: ${message}`, meta, childOptions),
|
|
235
|
+
log: (level, message, meta = {}) => self._log(level, `${modulePrefix}: ${message}`, meta, childOptions)
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// For other metadata without module prefix
|
|
240
|
+
const childLogger = {
|
|
241
|
+
error: (messageOrError, meta = {}) => self._log('error', messageOrError, { ...cleanMeta, ...meta }, childOptions),
|
|
242
|
+
warn: (messageOrError, meta = {}) => self._log('warn', messageOrError, { ...cleanMeta, ...meta }, childOptions),
|
|
243
|
+
info: (message, meta = {}) => self._log('info', message, { ...cleanMeta, ...meta }, childOptions),
|
|
244
|
+
debug: (message, meta = {}) => self._log('debug', message, { ...cleanMeta, ...meta }, childOptions),
|
|
245
|
+
verbose: (message, meta = {}) => self._log('verbose', message, { ...cleanMeta, ...meta }, childOptions),
|
|
246
|
+
log: (level, message, meta = {}) => self._log(level, message, { ...cleanMeta, ...meta }, childOptions)
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
return childLogger;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setLevel(level) {
|
|
253
|
+
this.options.level = level;
|
|
254
|
+
this.logger.transports.forEach(transport => {
|
|
255
|
+
transport.level = level;
|
|
256
|
+
});
|
|
257
|
+
this.info(`Log level changed to ${level}`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
setConsoleErrors(enabled) {
|
|
261
|
+
this.options.consoleErrors = enabled;
|
|
262
|
+
this.info(`Console errors ${enabled ? 'enabled' : 'disabled'}`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
setTelnetErrors(enabled) {
|
|
266
|
+
this.options.telnetErrors = enabled;
|
|
267
|
+
this.info(`Telnet errors ${enabled ? 'enabled' : 'disabled'}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
stream() {
|
|
271
|
+
return {
|
|
272
|
+
write: (message) => {
|
|
273
|
+
this.info(message.trim());
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = Logger;
|