I needed to add logging functionality to an existing TypeScript application hosted on ASP.Net MVC.
Since I usually use log4net in C#, log4javascript was an obvious choice, as it provides a similar API.
First, I needed to declare the parts of the log4javascript API I use in TypeScript:
declare module log4javascript { class Logger { info(msg: string); info(msg: string, value: any); addAppender(app: AjaxAppender); } function getLogger(name: string): Logger; function getNullLogger(): Logger; class AjaxAppender { constructor(url: string); setSessionId(id: string); setLayout(layout: JsonLayout); addHeader(key: string, value: string); } class JsonLayout { constructor(readable: boolean, combine: boolean); } }
In the logging class, I added logger and AjaxAppender. Depending on the data to log, I also configure a JsonLayout.
logger: log4javascript.Logger;
A non-logging logger instance is created using getNullLogger():
this.logger = log4javascript.getNullLogger();
Plain-text AjaxAppender
Use these statements to create an Ajax-based logger in TypeScript:
this.logger = log4javascript.getLogger("MyLoggerName"); var app = new log4javascript.AjaxAppender("http://myhost/myapp/mylogger"); this.logger.addAppender(app);
On the MVC side, the controller receives the parameters: logger, timestamp, level, url, message, sessionid, and layout. (Note that these parameter names can be changed using the setKeys() method)
The value for sessionid is set using
ajaxAppender.setSessionId(sessionId);
To log a string message, simply write
this.logger.info("my info log message");
The C# controller method stub therefore looks something like this:
[ValidateInput(false)] [HttpPost] public ActionResult Log(long? timestamp, string level, string sessionid, string message) { var ipaddr = Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; if (string.IsNullOrEmpty(ipaddr)) ipaddr = Request.ServerVariables["REMOTE_ADDR"]; if (string.IsNullOrEmpty(ipaddr)) ipaddr = Request.UserHostAddress; if (level == "INFO") logger.Info(ipaddr + " " + sessionid + " " + message); else logger.Debug("[" + level + "] " + ipaddr + " " + sessionid + " " + message); return Json(null); }
where logger is an instance of log4net.Ilog.
JSON-formatted AjaxAppender
To change the appender’s data format to JSON, we set its layout to JsonLayout:
this.logger = log4javascript.getLogger("MyJsonLogger"); var app = new log4javascript.AjaxAppender("http://myhost/myapp/myjsonlogger"); app.setLayout(new log4javascript.JsonLayout(false, false)); app.addHeader("Content-Type", "application/json"); this.logger.addAppender(app);
The C# controller method receives a JSON array of log entries
[ { "logger":"MyJsonLogger", "timestamp":1448621329758, "level":"INFO", "url":"http://localhost/myapp/...", "message": [ "message1", "message2", ... ] } ]
where message1, message2, etc are the parameters of the logger’s debug/info/warn etc. calls.
We create a C# class containing the required properties
public class Data { public string logger; public long? timestamp; public string level; public string url; public string[] message; }
and deserialize the HTTP request’s InputStream using JSON.Net:
[ValidateInput(false)] [HttpPost] public ActionResult Execute() { string inputContent; using (var sr = new StreamReader(HttpContext.Request.InputStream)) { inputContent = sr.ReadToEnd(); } var data = JsonConvert.DeserializeObject<Data[]>(inputContent); if (data != null) { foreach (var d in data) { if (d.level == "INFO") logger.Info(d.message[0] + " " + d.message[1]); else logger.Debug("[" + d.level + "] " + d.message[0] + " " + d.message[1]); } } return Json(null); }
Again, logger is an instance of log4net.Ilog.