-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogBuffer.cpp
More file actions
138 lines (122 loc) · 4.14 KB
/
Copy pathlogBuffer.cpp
File metadata and controls
138 lines (122 loc) · 4.14 KB
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
#include "logBuffer.h"
#include "logShipper.h" // optional remote log push (no-op unless configured)
LogBuffer Log;
static const size_t LOG_RING_SIZE = 6144;
static char ring[LOG_RING_SIZE];
static size_t head = 0; // next write position
static bool wrapped = false;
static bool atLineStart = true;
static volatile uint32_t lineVersion = 0;
static volatile uint32_t bytesWritten = 0;
// Spinlock, not a FreeRTOS mutex: writes come from both cores and must also
// work before the logger has been "begun" (there is no begin - static init
// covers everything).
static portMUX_TYPE logMux = portMUX_INITIALIZER_UNLOCKED;
static inline void ringPut(char c)
{
ring[head] = c;
head = (head + 1) % LOG_RING_SIZE;
if (head == 0) wrapped = true;
}
// UTC epoch minus uptime seconds, set after the first NTP sync. Lines are
// stamped with real time-of-day (suffix Z) once known; plain uptime before.
// Word-sized so the lock-free read in ringWrite below is atomic.
static volatile uint32_t wallClockOffset = 0;
void logSetWallClock(uint32_t utcEpochNow)
{
wallClockOffset = utcEpochNow - millis() / 1000UL;
}
// Callers hold logMux. CRs are dropped and each line gets a timestamp so
// entries can be correlated without a serial monitor attached. No ezTime
// calls here - this runs from both cores inside a spinlock, so the wall
// clock is plain arithmetic on the cached offset.
static void ringWrite(const uint8_t *buffer, size_t size)
{
for (size_t i = 0; i < size; i++)
{
char c = (char)buffer[i];
if (c == '\r') continue;
if (atLineStart && c != '\n')
{
char stamp[14];
unsigned long s = millis() / 1000UL;
uint32_t off = wallClockOffset;
if (off != 0)
{
uint32_t t = off + s;
snprintf(stamp, sizeof(stamp), "[%02lu:%02lu:%02luZ] ",
(unsigned long)((t / 3600UL) % 24UL),
(unsigned long)((t / 60UL) % 60UL),
(unsigned long)(t % 60UL));
}
else
{
snprintf(stamp, sizeof(stamp), "[%02lu:%02lu:%02lu] ",
(s / 3600UL) % 100UL, (s / 60UL) % 60UL, s % 60UL);
}
for (const char *p = stamp; *p; p++) ringPut(*p);
atLineStart = false;
}
if (c == '\n')
{
atLineStart = true;
lineVersion = lineVersion + 1;
}
ringPut(c);
}
}
size_t LogBuffer::write(uint8_t c)
{
return write(&c, 1);
}
size_t LogBuffer::write(const uint8_t *buffer, size_t size)
{
// Serial first (it may block on a full TX buffer - do that outside the
// spinlock), then the ring.
size_t n = Serial.write(buffer, size);
portENTER_CRITICAL(&logMux);
ringWrite(buffer, size);
bytesWritten = bytesWritten + size;
portEXIT_CRITICAL(&logMux);
// Tee into the remote-shipping queue (its own lock; never nested with
// logMux, and a no-op when LOG_PUSH_URL isn't configured).
logShipperFeed(buffer, size);
return n;
}
uint32_t logVersion()
{
return lineVersion; // 32-bit reads are atomic on the ESP32
}
uint32_t logBytesWritten()
{
return bytesWritten;
}
String logTail(size_t maxBytes)
{
if (maxBytes > LOG_RING_SIZE) maxBytes = LOG_RING_SIZE;
// Allocate outside the critical section (heap ops must not run with
// interrupts disabled), memcpy inside it.
char *tmp = (char *)malloc(maxBytes + 1);
if (!tmp) return String();
portENTER_CRITICAL(&logMux);
size_t avail = wrapped ? LOG_RING_SIZE : head;
size_t want = (maxBytes < avail) ? maxBytes : avail;
size_t start = (head + LOG_RING_SIZE - want) % LOG_RING_SIZE;
for (size_t i = 0; i < want; i++)
{
tmp[i] = ring[(start + i) % LOG_RING_SIZE];
}
bool clipped = (want < avail) || wrapped;
portEXIT_CRITICAL(&logMux);
tmp[want] = '\0';
// If the snapshot starts mid-line, drop the partial first line.
char *text = tmp;
if (clipped)
{
char *nl = strchr(tmp, '\n');
if (nl && *(nl + 1) != '\0') text = nl + 1;
}
String out(text);
free(tmp);
return out;
}