diff --git a/browserid/app.js b/browserid/app.js
index c62b6a2d8ec9dc419cb9c7a3e7d0805825691083..e0519db1adfeb417e0776bf623e4de709708c03d 100644
--- a/browserid/app.js
+++ b/browserid/app.js
@@ -18,6 +18,7 @@ secrets = require('./lib/secrets.js'),
 db = require('./lib/db.js'),
 configuration = require('../libs/configuration.js'),
 substitution = require('../libs/substitute.js');
+logging = require("../libs/logging.js");
 
 // looks unused, see run.js
 // const STATIC_DIR = path.join(path.dirname(__dirname), "static");
diff --git a/libs/logging.js b/libs/logging.js
new file mode 100644
index 0000000000000000000000000000000000000000..e166f27c518d249a9e495c9e78d9981b7c517b2b
--- /dev/null
+++ b/libs/logging.js
@@ -0,0 +1,28 @@
+const winston = require("winston");
+const configuration = require("./configuration");
+
+// go through the configuration and determine log location
+// for now we only log to one place
+// FIXME: separate logs depending on purpose?
+
+var log_path = configuration.get('log_path');
+var LOGGER = null;
+if (log_path) {
+  LOGGER= new (winston.Logger)({
+      transports: [new (winston.transports.File)({filename: log_path})]
+    });
+}
+
+// entry is an object that will get JSON'ified
+exports.log = function(entry) {
+  // entry must have at least a type
+  if (!entry.type)
+    throw new Error("every log entry needs a type");
+
+  // if no logger, go to console (FIXME: do we really want to log to console?)
+  if (LOGGER)
+    LOGGER.info(JSON.stringify(entry));
+  else
+    winston.info(JSON.stringify(entry));
+};
+
diff --git a/package.json b/package.json
index 78fdad42fca3a034200031b76e68b1a78cbccad4..85c35908c725152607c0677f34bc86688554478d 100644
--- a/package.json
+++ b/package.json
@@ -15,5 +15,6 @@
     , "temp": "0.2.0"
     , "express-csrf": "0.3.2"
     , "uglify-js": "1.0.6"
+    , "winston" : "0.3.3"
   }
 }
\ No newline at end of file