Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support formatting of metadata, including 'flat' builtin #5

Open
wants to merge 1 commit into
base: 1.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ Then you didn't specify a suitable instrumentation key. See the section above.

* **level**: lowest logging level transport to be logged (default: `info`)
* **silent**: Boolean flag indicating whether to suppress output (default: `false`)
* **treatErrorsAsExceptions**: Boolean flag indicating whether to treat errors as exceptions.
* **formatMeta**: Function that can be used to pre-process the log metadata before it is sent to azure. As application insights doesn't support nested properties, you can use the special value `"flat"` to flatten nested properties with underscores (default: metadata is not modified)
* **treatErrorsAsExceptions**: Boolean flag indicating whether to treat errors as exceptions.
See section below for more details (default: `false`).

**SDK integration options (required):**
Expand Down
150 changes: 92 additions & 58 deletions lib/winston-azure-application-insights.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,108 +9,140 @@ var WINSTON_LOGGER_NAME = 'applicationinsightslogger';
var WINSTON_DEFAULT_LEVEL = 'info';
var DEFAULT_IS_SILENT = false;

function noOp(x) {
return x;
}

// Remaping winston level on Application Insights
function flattenObject(source, target, prefix) {
Object.keys(source).forEach(function(key) {
var sourceVal = source[key];
var fullKey = prefix + '_' + key;
if (sourceVal && typeof sourceVal === "object") {
flattenObject(sourceVal, target, fullKey)
} else {
target[fullKey] = sourceVal;
}
});
}

function flattenMeta(meta) {
var flat = {};
Object.keys(meta).forEach(function(key) {
var val = meta[key];
if (val && typeof val === "object") {
flattenObject(val, flat, key);
} else {
flat[key] = val;
}
})
return flat;
}

// Remaping winston level on Application Insights
function getMessageLevel(winstonLevel) {

// TODO: Find a way to get the actual level values from AI's SDK
// They are defined in SDK's "Library/Contracts.ts"

var levels = {
emerg: 4, // AI 'Critical'
alert: 4, // AI 'Critical'
crit: 4, // AI 'Critical'
error: 3, // AI 'Error'
warning: 2, // AI 'Warning'
emerg: 4, // AI 'Critical'
alert: 4, // AI 'Critical'
crit: 4, // AI 'Critical'
error: 3, // AI 'Error'
warning: 2, // AI 'Warning'
warn: 2, // AI 'Warning'
notice: 1, // AI 'Informational'
info: 1, // AI 'Informational'
verbose: 0, // AI 'Verbose'
debug: 0, // AI 'Verbose'
silly: 0 // AI 'Verbose'
};
return winstonLevel in levels ? levels[winstonLevel] : levels.info;
}

return winstonLevel in levels ? levels[winstonLevel] : levels.info;
}


var AzureApplicationInsightsLogger = function (options) {

options = options || {};

winston.Transport.call(this, options);

if (options.client) {

// If client is set, just use it.
// We expect it to be already configured and started
this.client = options.client

} else if (options.insights) {

// If insights is set, just use the default client
// We expect it to be already configured and started
this.client = options.insights.client;

} else {
// Setup insights and start it
// If options.key is defined, use it. Else the SDK will expect
// an environment variable to be set.

appInsights
.setup(options.key)
.start();
this.client = appInsights.client;

this.client = appInsights.client;
}

if (!this.client) {
throw new Error('Could not get an Application Insights client instance');
}

this.name = WINSTON_LOGGER_NAME;
this.level = options.level || WINSTON_DEFAULT_LEVEL;
this.silent = options.silent || DEFAULT_IS_SILENT;
this.treatErrorsAsExceptions = !!options.treatErrorsAsExceptions;

this.formatMeta = options.formatMeta || noOp;
if (this.formatMeta === "flat") {
this.formatMeta = flattenMeta;
}

// Setup AI here!
};

//
// Inherits from 'winston.Transport'.
//
util.inherits(AzureApplicationInsightsLogger, winston.Transport);

//
// Define a getter so that 'winston.transport.AzureApplicationInsightsLogger'
// is available and thus backwards compatible
//
winston.transports.AzureApplicationInsightsLogger = AzureApplicationInsightsLogger;

//
// ### function log (level, msg, [meta], callback)
// #### @level {string} Level at which to log the message.
// #### @msg {string} Message to log
// #### @meta {Object} **Optional** Additional metadata to attach
// #### @callback {function} Continuation to respond to when complete.
// Core logging method exposed to Winston. Metadata is optional.
//
//
// Inherits from 'winston.Transport'.
//
util.inherits(AzureApplicationInsightsLogger, winston.Transport);

//
// Define a getter so that 'winston.transport.AzureApplicationInsightsLogger'
// is available and thus backwards compatible
//
winston.transports.AzureApplicationInsightsLogger = AzureApplicationInsightsLogger;

//
// ### function log (level, msg, [meta], callback)
// #### @level {string} Level at which to log the message.
// #### @msg {string} Message to log
// #### @meta {Object} **Optional** Additional metadata to attach
// #### @callback {function} Continuation to respond to when complete.
// Core logging method exposed to Winston. Metadata is optional.
//

AzureApplicationInsightsLogger.prototype.log = function (level, msg, meta, callback) {
if (typeof meta === 'function') {
callback = meta;
meta = {};

if (typeof meta === 'function') {
callback = meta;
meta = {};
}

callback = callback || function(){};
if (this.silent) {
return callback(null, true);
}

if (this.silent) {
return callback(null, true);
}

var aiLevel = getMessageLevel(level);

if (this.treatErrorsAsExceptions && aiLevel >= getMessageLevel('error')) {
var error;
if (msg instanceof Error) {
Expand All @@ -121,7 +153,7 @@ AzureApplicationInsightsLogger.prototype.log = function (level, msg, meta, callb
} else {
error = Error(msg);
}
this.client.trackException(error, meta);
this.client.trackException(error, this.formatMeta(meta));
}
else
{
Expand All @@ -130,24 +162,26 @@ AzureApplicationInsightsLogger.prototype.log = function (level, msg, meta, callb
stack: meta.stack,
name: meta.name
};

for (var field in meta) {
if(field === 'message' && !msg) {
msg = meta[field];
continue;
} else if (field === 'constructor') {
continue;
}

errorMeta[field] = meta[field];
}

meta = errorMeta;
}
this.client.trackTrace(msg, aiLevel, meta);
this.client.trackTrace(msg, aiLevel, this.formatMeta(meta));
}

return callback(null, true);
};

exports.AzureApplicationInsightsLogger = AzureApplicationInsightsLogger;

exports.flattenMeta = flattenMeta;
Loading