summaryrefslogtreecommitdiff
path: root/src/Debug.vala
blob: 186a1758b69e837633f644cad4fed16f688f2137 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* Copyright 2016 Software Freedom Conservancy Inc.
 *
 * This software is licensed under the GNU LGPL (version 2.1 or later).
 * See the COPYING file in this distribution.
 */

namespace Debug {
    private const LogLevelFlags DEFAULT_LOG_MASK =
        LogLevelFlags.LEVEL_CRITICAL |
        LogLevelFlags.LEVEL_WARNING |
        LogLevelFlags.LEVEL_MESSAGE;
    
    public const string VIEWER_PREFIX = "V";
    public const string LIBRARY_PREFIX = "L";
    
    // Ideally, there would be a LogLevelFlags.NONE constant to use as
    // empty value but failing that, 0 works as well
    private LogLevelFlags log_mask = 0;
    private string log_app_version_prefix;
    // log_file_stream is the canonical reference to the file stream (and owns
    // it), while log_out and log_err are indirections that can point to
    // log_file_stream or stdout and stderr respectively
    private unowned FileStream log_out = null;
    private unowned FileStream log_err = null;
    private FileStream log_file_stream = null;
    
    public static void init(string app_version_prefix) {
        log_app_version_prefix = app_version_prefix;
        
        // default to stdout/stderr if file cannot be opened or console is specified
        log_out = stdout;
        log_err = stderr;
        
        string log_file_error_msg = null;
        
        // logging to disk is currently off for viewer more; see https://bugzilla.gnome.org/show_bug.cgi?id=716474
        File? log_file = (log_app_version_prefix == LIBRARY_PREFIX) ? AppDirs.get_log_file() : null;
        if(log_file != null) {
            File log_dir = log_file.get_parent();
            try {
                if (log_dir.query_exists(null) == false) {
                    if (!log_dir.make_directory_with_parents(null)) {
                        log_file_error_msg = "Unable to create data directory %s".printf(log_dir.get_path());
                    }
                } 
            } catch (Error err) {
                log_file_error_msg = err.message;
            }
            // overwrite the log file every time the application is started
            // to ensure it doesn't grow too large; if there is a need for
            // keeping the log, the 'w' should be replaced by 'a' and some sort
            // of log rotation implemented
            log_file_stream = FileStream.open(log_file.get_path(), "w");
            if(log_file_stream != null) {
                log_out = log_file_stream;
                log_err = log_file_stream;
            } else {
                log_file_error_msg = "Unable to open or create log file %s".printf(log_file.get_path());
            }
        }
        
        if (Environment.get_variable("SHOTWELL_LOG") != null) {
            log_mask = LogLevelFlags.LEVEL_MASK;
        } else {
            log_mask = ((Environment.get_variable("SHOTWELL_INFO") != null) ?
                log_mask | LogLevelFlags.LEVEL_INFO :
                log_mask);
            log_mask = ((Environment.get_variable("SHOTWELL_DEBUG") != null) ?
                log_mask | LogLevelFlags.LEVEL_DEBUG :
                log_mask);
            log_mask = ((Environment.get_variable("SHOTWELL_MESSAGE") != null) ?
                log_mask | LogLevelFlags.LEVEL_MESSAGE :
                log_mask);
            log_mask = ((Environment.get_variable("SHOTWELL_WARNING") != null) ?
                log_mask | LogLevelFlags.LEVEL_WARNING :
                log_mask);
            log_mask = ((Environment.get_variable("SHOTWELL_CRITICAL") != null) ?
                log_mask | LogLevelFlags.LEVEL_CRITICAL :
                log_mask);
        }

        Log.set_handler(null, LogLevelFlags.LEVEL_INFO, info_handler);
        Log.set_handler(null, LogLevelFlags.LEVEL_DEBUG, debug_handler);
        Log.set_handler(null, LogLevelFlags.LEVEL_MESSAGE, message_handler);
        Log.set_handler(null, LogLevelFlags.LEVEL_WARNING, warning_handler);
        Log.set_handler(null, LogLevelFlags.LEVEL_CRITICAL, critical_handler);
        
        if(log_mask == 0 && log_file != null) {
            // if the log mask is still 0 and we have a log file, set the
            // mask to the default
            log_mask = DEFAULT_LOG_MASK;
        }
        
        if(log_file_error_msg != null) {
            warning("%s", log_file_error_msg);
        }
    }
    
    public static void terminate() {
    }
    
    private bool is_enabled(LogLevelFlags flag) {
        return ((log_mask & flag) > 0);
    }
    
    private void log(FileStream stream, string prefix, string message) {
        stream.printf("%s %d %s [%s] %s\n",
            log_app_version_prefix,
            Posix.getpid(),
            new DateTime.now(Application.timezone).format("%F %T"),
            prefix,
            message
        );
        stream.flush();
    }
    
    private void info_handler(string? domain, LogLevelFlags flags, string message) {
        if (is_enabled(LogLevelFlags.LEVEL_INFO))
            log(log_out, "INF", message);
    }
    
    private void debug_handler(string? domain, LogLevelFlags flags, string message) {
        if (is_enabled(LogLevelFlags.LEVEL_DEBUG))
            log(log_out, "DBG", message);
    }
    
    private void message_handler(string? domain, LogLevelFlags flags, string message) {
        if (is_enabled(LogLevelFlags.LEVEL_MESSAGE))
            log(log_err, "MSG", message);
    }

    private void warning_handler(string? domain, LogLevelFlags flags, string message) {
        if (is_enabled(LogLevelFlags.LEVEL_WARNING))
            log(log_err, "WRN", message);
    }

    private void critical_handler(string? domain, LogLevelFlags flags, string message) {
        if (is_enabled(LogLevelFlags.LEVEL_CRITICAL)) {
            log(log_err, "CRT", message);
            if (log_file_stream != null)
                log(stderr, "CRT", message);    // also log to console
        }
    }
}