1 
2 module logging.facade;
3 
4 public import logging.formatters;
5 public import logging.levels;
6 public import logging.sinks;
7 
8 private import std.datetime;
9 private import std.string;
10 private import logging.utils;
11 
12 public struct log
13 {
14 	public static void log(LogLevel loglevel, string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args)
15 	{
16 		_facade.log(loglevel, m, func, line, Clock.currTime(), fmt.format(args), abbr_thread_id());
17 	}
18 
19 	public static void error(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args)
20 	{
21 		log!(LogLevel.Error,m,func,line,Args)(fmt, args);
22 	}
23 
24 	public static void warning(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args)
25 	{
26 		log!(LogLevel.Warning,m,func,line,Args)(fmt, args);
27 	}
28 
29 	public alias warning warn;
30 
31 	public static void info(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args)
32 	{
33 		log!(LogLevel.Info,m,func,line,Args)(fmt, args);
34 	}
35 
36 	public static void debg(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args)
37 	{
38 		log!(LogLevel.Debug,m,func,line,Args)(fmt, args);
39 	}
40 
41 	public static void trace(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__, Args...)(string fmt, Args args)
42 	{
43 		debug log!(LogLevel.Trace,m,func,line,Args)(fmt, args);
44 	}
45 
46 	public static void trace(string m = __MODULE__, string func = __FUNCTION__, size_t line = __LINE__)()
47 	{
48 		debug log!(LogLevel.Trace,m,func,line)("trace");
49 	}
50 
51 	public static void add_sink(LogSink s)
52 	{
53 		_facade.add_sink(s);
54 	}
55 
56 	public static void remove_all_sinks()
57 	{
58 		_facade.remove_all_sinks();
59 	}
60 
61 	shared static this()
62 	{
63 		_facade = new FacadeImpl();
64 	}
65 
66 	__gshared FacadeImpl _facade;
67 }
68 
69 // not quite beautiful: couldnt overcome type system for synchronized class
70 
71 private class FacadeImpl
72 {
73 	public void log(LogLevel loglevel, string m, string func, size_t line, SysTime time, lazy string msg, uint thread_id)
74 	{
75 		synchronized(this)
76 		{
77 			foreach(l; _sinks)
78 			{
79 				l.log(loglevel, m, func, line, Clock.currTime(), msg, abbr_thread_id());
80 			}
81 		}
82 	}
83 
84 	public void add_sink(LogSink sink)
85 	{
86 		synchronized(this)
87 		{
88 			_sinks ~= sink;
89 		}
90 	}
91 
92 	public void remove_all_sinks()
93 	{
94 		synchronized(this)
95 		{
96 			_sinks = null;
97 		}
98 	}
99 
100 	private LogSink[] _sinks;
101 }
102 
103 unittest
104 {
105 	import std.concurrency;
106 	import std.stdio;
107 	import std.file;
108 	import std.uuid;
109 
110 	static class ThreadedFormatter : Formatter
111 	{
112 		override string format(LogLevel loglevel, string m, string func, size_t line, SysTime st, string msg, uint thread_id)
113 		{
114 			return "T#%02s %s %s".format(abbr_thread_id(), LOGLEVEL_STR[loglevel], msg);
115 		}
116 	}
117 
118 	static void worker(int i)
119 	{
120 		log.info("worker %s", i);
121 		send(ownerTid(),1);
122 	}
123 
124 	log.remove_all_sinks();
125 	log.info("should not be logged");
126 
127 	string tmpnam = randomUUID().toString();
128 	log.add_sink(new FileLogSink(tmpnam, new ThreadedFormatter));
129 
130 	log.info("should be logged from main thread");
131 
132 	auto tid = spawn(&worker, 1);
133 	receiveOnly!int();
134 
135 	log.remove_all_sinks();
136 
137 	auto text = readText(tmpnam);
138 
139 	auto expected_text = `T#00 INFO should be logged from main thread
140 T#01 INFO worker 1
141 `;
142 
143 	assert(text == expected_text);
144 }