Skip to content

Commit

Permalink
Merge pull request #179 from minrk/winston4
Browse files Browse the repository at this point in the history
update logging to winston 3 API
  • Loading branch information
minrk committed Oct 11, 2018
2 parents 01807e0 + 3d43685 commit b7fbe5f
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 66 deletions.
39 changes: 18 additions & 21 deletions bin/configurable-http-proxy
Expand Up @@ -5,13 +5,13 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
//
"use strict";

var fs = require("fs"),
pkg = require("../package.json"),
args = require("commander"),
strftime = require("strftime"),
tls = require("tls"),
log = require("winston");
winston = require("winston");

args
.version(pkg.version)
Expand Down Expand Up @@ -44,8 +44,14 @@ args
"Reject unauthorized SSL connections (only meaningful if --api-ssl-request-cert is given)"
)
.option("--client-ssl-key <keyfile>", "SSL key to use, if any, for proxy to client requests")
.option("--client-ssl-cert <certfile>", "SSL certificate to use, if any, for proxy to client requests")
.option("--client-ssl-ca <ca-file>", "SSL certificate authority, if any, for proxy to client requests")
.option(
"--client-ssl-cert <certfile>",
"SSL certificate to use, if any, for proxy to client requests"
)
.option(
"--client-ssl-ca <ca-file>",
"SSL certificate authority, if any, for proxy to client requests"
)
.option("--client-ssl-request-cert", "Request SSL certs to authenticate clients for API requests")
.option(
"--client-ssl-reject-unauthorized",
Expand Down Expand Up @@ -90,19 +96,10 @@ args

args.parse(process.argv);

log.remove(log.transports.Console);
log.add(log.transports.Console, {
colorize: process.stdout.isTTY && process.stderr.isTTY,
level: args.logLevel.toLowerCase(),
timestamp: function() {
return strftime("%H:%M:%S.%L", new Date());
},
label: "ConfigProxy",
});

var ConfigurableProxy = require("../lib/configproxy.js").ConfigurableProxy;

var options = {};
var log = require("../lib/log.js").defaultLogger({ level: args.logLevel.toLowerCase() });
var options = { log: log };

var sslCiphers;
if (args.sslCiphers) {
Expand Down Expand Up @@ -182,13 +179,13 @@ if (args.apiSslKey || args.apiSslCert) {
if (args.apiSslCa) {
// When multiple CAs need to be specified, they must be broken into
// an array of certs unfortunately.
var chain = fs.readFileSync(args.apiSslCa, 'utf8');
var chain = fs.readFileSync(args.apiSslCa, "utf8");
var ca = [];
var cert = [];
chain.split('\n').forEach(function(line) {
chain.split("\n").forEach(function(line) {
cert.push(line);
if (line.match(/-END CERTIFICATE-/)) {
ca.push(new Buffer(cert.join('\n')));
ca.push(new Buffer(cert.join("\n")));
cert = [];
}
});
Expand Down Expand Up @@ -217,13 +214,13 @@ if (args.clientSslKey || args.clientSslCert) {
if (args.clientSslCa) {
// When multiple CAs need to be specified, they must be broken into
// an array of certs unfortunately.
var chain = fs.readFileSync(args.clientSslCa, 'utf8');
var chain = fs.readFileSync(args.clientSslCa, "utf8");
var ca = [];
var cert = [];
chain.split('\n').forEach(function(line) {
chain.split("\n").forEach(function(line) {
cert.push(line);
if (line.match(/-END CERTIFICATE-/)) {
ca.push(new Buffer(cert.join('\n')));
ca.push(new Buffer(cert.join("\n")));
cert = [];
}
});
Expand Down
96 changes: 52 additions & 44 deletions lib/configproxy.js
Expand Up @@ -14,9 +14,10 @@ var http = require("http"),
path = require("path"),
EventEmitter = require("events").EventEmitter,
httpProxy = require("http-proxy"),
log = require("winston"),
winston = require("winston"),
util = require("util"),
URL = require("url"),
defaultLogger = require("./log").defaultLogger,
querystring = require("querystring");

function bound(that, method) {
Expand Down Expand Up @@ -83,7 +84,10 @@ function authorized(method) {
if (token === this.authToken) {
return method.apply(this, arguments);
} else {
log.debug("Rejecting API request from: %s", req.headers.authorization || "no authorization");
this.log.debug(
"Rejecting API request from: %s",
req.headers.authorization || "no authorization"
);
res.writeHead(403);
res.end();
}
Expand All @@ -98,21 +102,6 @@ function parseHost(req) {
return host;
}

function logResponse(req, res) {
// log function called when any response is finished
var code = res.statusCode;
var logF;
if (code < 400) {
logF = log.info;
} else if (code < 500) {
logF = log.warn;
} else {
logF = log.error;
}
var msg = res._logMsg || "";
logF("%s %s %s %s", code, req.method.toUpperCase(), req.url, msg);
}

function camelCaseify(options) {
// camelCaseify options dict, for backward compatibility
let camelOptions = {};
Expand All @@ -121,7 +110,7 @@ function camelCaseify(options) {
return part.toUpperCase();
});
if (camelKey !== key) {
log.warn("option %s is deprecated, use %s.", key, camelKey);
this.log.warn("option %s is deprecated, use %s.", key, camelKey);
}
camelOptions[camelKey] = options[key];
});
Expand All @@ -143,7 +132,11 @@ class ConfigurableProxy extends EventEmitter {
constructor(options) {
super();
var that = this;
this.options = camelCaseify(options || {});
this.log = (options || {}).log;
if (!this.log) {
this.log = defaultLogger();
}
this.options = camelCaseify.apply(this, [options || {}]);

this._routes = loadStorage(options || {});
this.authToken = this.options.authToken;
Expand Down Expand Up @@ -199,10 +192,10 @@ class ConfigurableProxy extends EventEmitter {
],
];

var logErrors = function(handler) {
var logErrors = handler => {
return function(req, res) {
function logError(e) {
log.error("Error in handler for " + req.method + " " + req.url + ": ", e);
that.log.error("Error in handler for " + req.method + " " + req.url + ": %s", e);
}
try {
let p = handler.apply(that, arguments);
Expand Down Expand Up @@ -238,13 +231,28 @@ class ConfigurableProxy extends EventEmitter {
});
}

logResponse(req, res) {
// log function called when any response is finished
var code = res.statusCode;
var logF;
if (code < 400) {
logF = this.log.info;
} else if (code < 500) {
logF = this.log.warn;
} else {
logF = this.log.error;
}
var msg = res._logMsg || "";
logF("%s %s %s %s", code, req.method.toUpperCase(), req.url, msg);
}

addRoute(path, data) {
// add a route to the routing table
path = this._routes.cleanPath(path);
if (this.hostRouting && path !== "/") {
data.host = path.split("/")[1];
}
log.info("Adding route %s -> %s", path, data.target);
this.log.info("Adding route %s -> %s", path, data.target);

var that = this;

Expand All @@ -259,7 +267,7 @@ class ConfigurableProxy extends EventEmitter {

return routes.get(path).then(result => {
if (result) {
log.info("Removing route %s", path);
this.log.info("Removing route %s", path);
return routes.remove(path);
}
});
Expand Down Expand Up @@ -333,7 +341,7 @@ class ConfigurableProxy extends EventEmitter {
path = path || "/";

if (typeof data.target !== "string") {
log.warn("Bad POST data: %s", JSON.stringify(data));
this.log.warn("Bad POST data: %s", JSON.stringify(data));
fail(req, res, 400, "Must specify 'target' as string");
return;
}
Expand Down Expand Up @@ -427,7 +435,7 @@ class ConfigurableProxy extends EventEmitter {
errMsg = e;
}
}
log.error("%s %s %s", code, req.method, req.url, errMsg);
this.log.error("%s %s %s %s", code, req.method, req.url, errMsg);
if (!res) {
// socket-level error, no response to build
return;
Expand All @@ -444,34 +452,34 @@ class ConfigurableProxy extends EventEmitter {
if (res.setHeader) res.setHeader(key, upstream.headers[key]);
});
if (res.writeHead) res.writeHead(code);
upstream.on("data", function(data) {
upstream.on("data", data => {
if (res.write) res.write(data);
});
upstream.on("end", function() {
upstream.on("end", () => {
if (res.end) res.end();
});
});
errorRequest.on("error", function(e) {
errorRequest.on("error", e => {
// custom error failed, fallback on default
log.error("Failed to get custom error page", e);
proxy._handleProxyErrorDefault(code, kind, req, res);
this.log.error("Failed to get custom error page: %s", e);
this._handleProxyErrorDefault(code, kind, req, res);
});
errorRequest.end();
} else if (this.errorPath) {
var filename = path.join(this.errorPath, code.toString() + ".html");
if (!fs.existsSync(filename)) {
log.debug("No error file %s", filename);
this.log.debug("No error file %s", filename);
filename = path.join(this.errorPath, "error.html");
if (!fs.existsSync(filename)) {
log.error("No error file %s", filename);
proxy._handleProxyErrorDefault(code, kind, req, res);
this.log.error("No error file %s", filename);
this._handleProxyErrorDefault(code, kind, req, res);
return;
}
}
fs.readFile(filename, function(err, data) {
fs.readFile(filename, (err, data) => {
if (err) {
log.error("Error reading %s %s", filename, err);
proxy._handleProxyErrorDefault(code, kind, req, res);
this.log.error("Error reading %s %s", filename, err);
this._handleProxyErrorDefault(code, kind, req, res);
return;
}
if (res.writeHead) res.writeHead(code, { "Content-Type": "text/html" });
Expand All @@ -489,7 +497,7 @@ class ConfigurableProxy extends EventEmitter {
var args = Array.prototype.slice.call(arguments, 1);

// get the proxy target
return this.targetForReq(req).then(function(match) {
return this.targetForReq(req).then(match => {
if (!match) {
that.handleProxyError(404, kind, req, res);
return;
Expand All @@ -498,16 +506,16 @@ class ConfigurableProxy extends EventEmitter {
that.emit("proxyRequest", req, res);
var prefix = match.prefix;
var target = match.target;
log.debug("PROXY", kind.toUpperCase(), req.url, "to", target);
this.log.debug("PROXY %s %s to %s", kind.toUpperCase(), req.url, target);
if (!that.includePrefix) {
req.url = req.url.slice(prefix.length);
}

target = URL.parse(target);
if(that.options.clientSsl){
target.key = that.options.clientSsl.key;
target.cert = that.options.clientSsl.cert;
target.ca = that.options.clientSsl.ca;
if (that.options.clientSsl) {
target.key = that.options.clientSsl.key;
target.cert = that.options.clientSsl.cert;
target.ca = that.options.clientSsl.ca;
}

// add config argument
Expand Down Expand Up @@ -551,8 +559,8 @@ class ConfigurableProxy extends EventEmitter {
// Handle a request to the REST API
this.statsd.increment("requests.api", 1);
if (res) {
res.on("finish", function() {
logResponse(req, res);
res.on("finish", () => {
this.logResponse(req, res);
});
}
var args = [req, res];
Expand Down
25 changes: 25 additions & 0 deletions lib/log.js
@@ -0,0 +1,25 @@
"use strict";
var strftime = require("strftime"),
winston = require("winston");

const simpleFormat = winston.format.printf(info => {
// console.log(info);
return `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`;
});

function defaultLogger(options) {
options = options || {};
options.format = winston.format.combine(
winston.format.colorize(),
winston.format.label({ label: "ConfigProxy" }),
winston.format.splat(),
winston.format.timestamp({
format: () => strftime("%H:%M:%S.%L", new Date()),
}),
simpleFormat
);
options.transports = [new winston.transports.Console()];
return winston.createLogger(options);
}

exports.defaultLogger = defaultLogger;
4 changes: 3 additions & 1 deletion lib/testutil.js
Expand Up @@ -6,7 +6,8 @@ var extend = require("util")._extend;
var WebSocketServer = require("ws").Server;
var querystring = require("querystring");

var configproxy = require("../lib/configproxy");
var configproxy = require("./configproxy");
var defaultLogger = require("./log").defaultLogger;

var servers = [];

Expand Down Expand Up @@ -86,6 +87,7 @@ function addTargets(proxy, paths, port) {
exports.setupProxy = function(port, options, paths) {
options = options || {};
options.authToken = "secret";
options.log = defaultLogger({ level: "error" });

var proxy = new configproxy.ConfigurableProxy(options);
var ip = "127.0.0.1";
Expand Down

0 comments on commit b7fbe5f

Please sign in to comment.