libaaruformat
1.0
Aaru Data Preservation Suite - Format Library
Toggle main menu visibility
Loading...
Searching...
No Matches
log.c
Go to the documentation of this file.
1
/*
2
* This file is part of the Aaru Data Preservation Suite.
3
* Copyright (c) 2019-2026 Natalia Portillo.
4
*
5
* This library is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU Lesser General Public License as
7
* published by the Free Software Foundation; either version 2.1 of the
8
* License, or (at your option) any later version.
9
*
10
* This library is distributed in the hope that it will be useful, but
11
* WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
14
*
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#if !defined(_WIN32) && !defined(_POSIX_C_SOURCE)
20
#define _POSIX_C_SOURCE 200112L
// localtime_r
21
#endif
22
23
#include <stdarg.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <time.h>
28
29
#ifdef _WIN32
30
#include <windows.h>
31
#else
32
#include <sys/time.h>
33
#endif
34
35
#include "
log.h
"
36
37
#define LOG_STATE_UNINITIALIZED 0
38
#define LOG_STATE_INITIALIZING 1
39
#define LOG_STATE_READY 2
40
41
#define LOG_LEVEL_NONE (-1)
42
43
#define LOG_STACK_BUFFER_SIZE 1024
44
45
static
volatile
long
log_state
=
LOG_STATE_UNINITIALIZED
;
46
static
FILE *
log_stream
= NULL;
47
static
int
log_level
=
AARU_LOG_TRACE
;
48
49
/* Minimal portable atomics so the one-time initialization is race-free without pthreads. */
50
#if defined(_MSC_VER)
51
static
int
log_state_claim
(
void
) {
return
InterlockedCompareExchange(&
log_state
,
LOG_STATE_INITIALIZING
,
LOG_STATE_UNINITIALIZED
) ==
LOG_STATE_UNINITIALIZED
; }
52
static
long
log_state_load
(
void
) {
return
InterlockedCompareExchange(&
log_state
, 0, 0); }
53
static
void
log_state_store
(
long
value) { InterlockedExchange(&
log_state
, value); }
54
#elif defined(__GNUC__) || defined(__clang__)
55
static
int
log_state_claim
(
void
)
56
{
57
long
expected =
LOG_STATE_UNINITIALIZED
;
58
return
__atomic_compare_exchange_n(&
log_state
, &expected,
LOG_STATE_INITIALIZING
, 0, __ATOMIC_ACQ_REL,
59
__ATOMIC_ACQUIRE);
60
}
61
static
long
log_state_load
(
void
) {
return
__atomic_load_n(&
log_state
, __ATOMIC_ACQUIRE); }
62
static
void
log_state_store
(
long
value) { __atomic_store_n(&
log_state
, value, __ATOMIC_RELEASE); }
63
#else
64
static
int
log_state_claim
(
void
)
65
{
66
if
(
log_state
!=
LOG_STATE_UNINITIALIZED
)
return
0;
67
log_state
=
LOG_STATE_INITIALIZING
;
68
return
1;
69
}
70
static
long
log_state_load
(
void
) {
return
log_state
; }
71
static
void
log_state_store
(
long
value) {
log_state
= value; }
72
#endif
73
74
static
int
log_strcasecmp
(
const
char
*a,
const
char
*b)
75
{
76
while
(*a && *b)
77
{
78
int
ca = (
unsigned
char)*a++;
79
int
cb = (
unsigned
char)*b++;
80
if
(ca >=
'A'
&& ca <=
'Z'
) ca +=
'a'
-
'A'
;
81
if
(cb >=
'A'
&& cb <=
'Z'
) cb +=
'a'
-
'A'
;
82
if
(ca != cb)
return
ca - cb;
83
}
84
return
(
unsigned
char
)*a - (
unsigned
char)*b;
85
}
86
93
static
void
log_init
(
void
)
94
{
95
const
char
*level_env;
96
const
char
*file_env;
97
98
if
(
log_state_load
() ==
LOG_STATE_READY
)
return
;
99
100
if
(!
log_state_claim
())
101
{
102
// Another thread is initializing; the window is a getenv() and an fopen().
103
while
(
log_state_load
() !=
LOG_STATE_READY
);
104
return
;
105
}
106
107
level_env = getenv(
"AARUFORMAT_LOG_LEVEL"
);
108
if
(level_env != NULL)
109
{
110
if
(
log_strcasecmp
(level_env,
"none"
) == 0 ||
log_strcasecmp
(level_env,
"off"
) == 0)
111
log_level
=
LOG_LEVEL_NONE
;
112
else
if
(
log_strcasecmp
(level_env,
"fatal"
) == 0)
113
log_level
=
AARU_LOG_FATAL
;
114
else
115
log_level
=
AARU_LOG_TRACE
;
116
}
117
118
log_stream
= stderr;
119
file_env = getenv(
"AARUFORMAT_LOG_FILE"
);
120
if
(file_env != NULL && *file_env !=
'\0'
&&
log_level
!=
LOG_LEVEL_NONE
)
121
{
122
FILE *file = fopen(file_env,
"a"
);
123
if
(file != NULL)
log_stream
= file;
124
}
125
126
log_state_store
(
LOG_STATE_READY
);
127
}
128
130
static
const
char
*
log_short_path
(
const
char
*file)
131
{
132
const
char
*p;
133
const
char
*base = file;
134
135
for
(p = file; *p; p++)
136
{
137
if
((*p ==
'/'
|| *p ==
'\\'
) && strncmp(p + 1,
"src"
, 3) == 0 && (p[4] ==
'/'
|| p[4] ==
'\\'
))
138
base = p + 5;
139
}
140
141
if
(base != file)
return
base;
142
143
for
(p = file; *p; p++)
144
if
(*p ==
'/'
|| *p ==
'\\'
) base = p + 1;
145
146
return
base;
147
}
148
149
static
int
log_timestamp
(
char
*buf,
size_t
size)
150
{
151
#ifdef _WIN32
152
SYSTEMTIME st;
153
GetLocalTime(&st);
154
return
snprintf(buf, size,
"%02u:%02u:%02u.%03u"
, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
155
#else
156
struct
timeval tv;
157
struct
tm tm;
158
time_t secs;
159
160
gettimeofday(&tv, NULL);
161
secs = tv.tv_sec;
162
if
(localtime_r(&secs, &tm) == NULL)
return
snprintf(buf, size,
"??:??:??.???"
);
163
return
snprintf(buf, size,
"%02d:%02d:%02d.%03ld"
, tm.tm_hour, tm.tm_min, tm.tm_sec, (
long
)(tv.tv_usec / 1000));
164
#endif
165
}
166
173
void
aaru_log_write
(
int
level,
const
char
*file,
int
line,
const
char
*func,
const
char
*fmt, ...)
174
{
175
char
stack_buf[
LOG_STACK_BUFFER_SIZE
];
176
char
*buf = stack_buf;
177
char
timestamp[16];
178
const
char
*tag = level ==
AARU_LOG_FATAL
?
"FATAL"
:
"TRACE"
;
179
int
prefix_len;
180
int
msg_len;
181
size_t
total;
182
va_list args;
183
va_list args_copy;
184
185
log_init
();
186
187
if
(level >
log_level
)
return
;
188
189
log_timestamp
(timestamp,
sizeof
(timestamp));
190
191
prefix_len = snprintf(NULL, 0,
"%s [%s] %s:%d %s(): "
, timestamp, tag,
log_short_path
(file), line, func);
192
193
va_start(args, fmt);
194
va_copy(args_copy, args);
195
msg_len = vsnprintf(NULL, 0, fmt, args_copy);
196
va_end(args_copy);
197
198
if
(prefix_len < 0 || msg_len < 0)
199
{
200
va_end(args);
201
return
;
202
}
203
204
total = (size_t)prefix_len + (
size_t
)msg_len + 2;
// newline + terminator
205
206
if
(total >
sizeof
(stack_buf))
207
{
208
buf = malloc(total);
209
if
(buf == NULL)
210
{
211
va_end(args);
212
return
;
213
}
214
}
215
216
snprintf(buf, total,
"%s [%s] %s:%d %s(): "
, timestamp, tag,
log_short_path
(file), line, func);
217
vsnprintf(buf + prefix_len, total - (
size_t
)prefix_len, fmt, args);
218
va_end(args);
219
220
buf[total - 2] =
'\n'
;
221
buf[total - 1] =
'\0'
;
222
223
fwrite(buf, 1, total - 1,
log_stream
);
224
fflush(
log_stream
);
225
226
if
(buf != stack_buf) free(buf);
227
}
228
229
void
aaru_log_reset
(
void
)
230
{
231
if
(
log_state_load
() ==
LOG_STATE_READY
&&
log_stream
!= NULL &&
log_stream
!= stderr) fclose(
log_stream
);
232
233
log_stream
= NULL;
234
log_level
=
AARU_LOG_TRACE
;
235
log_state_store
(
LOG_STATE_UNINITIALIZED
);
236
}
log_state
static volatile long log_state
Definition
log.c:45
LOG_STATE_INITIALIZING
#define LOG_STATE_INITIALIZING
Definition
log.c:38
LOG_STACK_BUFFER_SIZE
#define LOG_STACK_BUFFER_SIZE
Definition
log.c:43
log_state_load
static long log_state_load(void)
Definition
log.c:70
log_strcasecmp
static int log_strcasecmp(const char *a, const char *b)
Definition
log.c:74
LOG_LEVEL_NONE
#define LOG_LEVEL_NONE
Definition
log.c:41
LOG_STATE_READY
#define LOG_STATE_READY
Definition
log.c:39
log_state_store
static void log_state_store(long value)
Definition
log.c:71
log_timestamp
static int log_timestamp(char *buf, size_t size)
Definition
log.c:149
LOG_STATE_UNINITIALIZED
#define LOG_STATE_UNINITIALIZED
Definition
log.c:37
log_init
static void log_init(void)
Resolves the logging configuration from the environment, exactly once per process.
Definition
log.c:93
log_short_path
static const char * log_short_path(const char *file)
Strips the build-machine directory from FILE, keeping the path below src/ if present.
Definition
log.c:130
log_state_claim
static int log_state_claim(void)
Definition
log.c:64
log_stream
static FILE * log_stream
Definition
log.c:46
log_level
static int log_level
Definition
log.c:47
log.h
AARU_LOG_FATAL
#define AARU_LOG_FATAL
Definition
log.h:37
AARU_LOG_TRACE
#define AARU_LOG_TRACE
Definition
log.h:38
aaru_log_reset
void aaru_log_reset(void)
Definition
log.c:229
aaru_log_write
void aaru_log_write(int level, const char *file, int line, const char *func, const char *fmt,...)
Writes one log line.
Definition
log.c:173
src
log.c
Generated by
1.18.0