1 /++
2  + Copyright: Copyright © 2017, Christian Köstlin
3  + License: MIT
4  + Authors: Christian Koestlin
5  +/
6 
7 module androidlogger;
8 
9 @safe:
10 
11 import std.experimental.logger;
12 public import androidlogger.packageversion;
13 
14 class AndroidLogger : FileLogger
15 {
16     import std.stdio;
17     import std..string;
18     import std.concurrency;
19     import std.process;
20     import colored;
21 
22     private string[LogLevel] logLevel2String;
23     private bool withColors;
24 
25     this(File file = stdout, bool withColors = true, LogLevel level = LogLevel.all) @system
26     {
27         super(file, level);
28         this.withColors = withColors;
29         initLogLevel2String();
30     }
31 
32     static string tid2string(Tid id) @trusted
33     {
34         import std.conv : text;
35 
36         return text(id).replace("Tid(", "").replace(")", "");
37     }
38 
39     override void writeLogMsg(ref LogEntry payload) @trusted
40     {
41         with (payload)
42         {
43             // android logoutput looks lokes this:
44             // 06-06 12:14:46.355 372 18641 D audio_hw_primary: disable_audio_route: reset and update
45             // DATE  TIME         PID TID   LEVEL TAG           Message
46             auto h = timestamp.fracSecs.split!("msecs");
47             auto idx = msg.indexOf(':');
48             string tag = ""; // "%s.%d".format(file, line),
49             string text = "";
50             if (idx == -1)
51             {
52                 tag = "stdout";
53                 text = msg;
54             }
55             else
56             {
57                 tag = msg[0 .. idx];
58                 text = msg[idx + 1 .. $];
59             }
60             this.file.lockingTextWriter()
61                 .put(colorize("%02d-%02d %02d:%02d:%02d.%03d %d %s %s %s: %s\n".format(timestamp.month, // DATE
62                         timestamp.day, timestamp.hour, // TIME
63                         timestamp.minute, timestamp.second,
64                         h.msecs, std.process.thisProcessID, // PID
65                         tid2string(threadId), // TID
66                         logLevel2String[logLevel], tag, text), logLevel).toString);
67 
68         }
69     }
70 
71     private void initLogLevel2String()
72     {
73         logLevel2String[LogLevel.trace] = "T";
74         logLevel2String[LogLevel.info] = "I";
75         logLevel2String[LogLevel.warning] = "W";
76         logLevel2String[LogLevel.error] = "E";
77         logLevel2String[LogLevel.critical] = "C";
78         logLevel2String[LogLevel.fatal] = "F";
79     }
80 
81     private auto colorize(string s, LogLevel logLevel)
82     {
83         if (!withColors)
84         {
85             return s.defaultColor.onDefaultColor;
86         }
87 
88         switch (logLevel)
89         {
90         case LogLevel.trace:
91             return s.black.onDefaultColor;
92         case LogLevel.info:
93             return s.defaultColor.onDefaultColor;
94         case LogLevel.warning:
95             return s.green.onDefaultColor;
96         case LogLevel.error:
97             return s.yellow.onDefaultColor;
98         case LogLevel.critical:
99             return s.black.onRed;
100         case LogLevel.fatal:
101             return s.yellow.onRed;
102         default:
103             assert(0);
104         }
105     }
106 }