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

update logging to winston 3 API #179

Merged
merged 1 commit into from Oct 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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