Skip to content

Commit c9bb302

Browse files
committed
Log data in influxdb log format
1 parent 71132c8 commit c9bb302

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ __pycache__/
66

77
# direnv python layout
88
.direnv
9+
10+
# log files
11+
*.log

influxdb_logger.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
2+
import logging
3+
import logging.handlers
4+
import os
5+
import time
6+
7+
class InfluxdbLogger:
8+
"""Repeatedly logs data from all devices"""
9+
LOG_OUTPUT_FILE = "measurements.log"
10+
11+
# the influxdb measurement we're outputting
12+
MEASUREMENT = "bmnode"
13+
14+
# how big should the log file get before rotating?
15+
MAX_SIZE_BYTES = 1024 * 1024 * 10 # 10 megabytes
16+
17+
# how many backup files to keep after rotating?
18+
# must be at least 1 to enable rotation
19+
MAX_BACKUP_FILES = 1
20+
21+
@property
22+
def datalog(self) -> logging.Logger:
23+
if not hasattr(self, "_datalog"):
24+
# make sure our destination exists
25+
try:
26+
os.makedirs(os.path.dirname(os.path.abspath(self.LOG_OUTPUT_FILE)))
27+
except FileExistsError:
28+
pass
29+
30+
# grab the data logger
31+
datalog = logging.getLogger("monitor.data")
32+
33+
# ignore any parent loggers -- these lines get written to file ONLY
34+
datalog.propagate = False
35+
36+
# log at INFO
37+
datalog.setLevel(logging.INFO)
38+
39+
# rotate the files every once in a while to allow files to close
40+
handler = logging.handlers.RotatingFileHandler(
41+
self.LOG_OUTPUT_FILE,
42+
maxBytes=self.MAX_SIZE_BYTES,
43+
backupCount=self.MAX_BACKUP_FILES,
44+
)
45+
datalog.addHandler(handler)
46+
47+
# save as root logger
48+
self._datalog = datalog
49+
50+
return self._datalog
51+
52+
@property
53+
def hostname(self) -> str:
54+
if not hasattr(self, "_hostname"):
55+
self._hostname = open("/etc/hostname").read().strip()
56+
return self._hostname
57+
58+
@classmethod
59+
def d2str(cls, d) -> str:
60+
"""convert dictionary of key/value pairs to a string"""
61+
pairs = [f"{k.replace(' ', '_')}={v}" for k,v in d.items()]
62+
return ",".join(pairs)
63+
64+
def emit(self, fields, tags, measurement=None) -> None:
65+
"""logs specified fields and tags in influxdb format"""
66+
# grab timestamp
67+
ts = time.time_ns()
68+
69+
# what's the measurement
70+
measurement = measurement if measurement else self.MEASUREMENT
71+
72+
# output the log;
73+
# https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/
74+
self.datalog.info(
75+
f"{self.MEASUREMENT},{self.d2str(tags)} {self.d2str(fields)} {ts}"
76+
)

main.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
import time
88
from typing import Tuple
99

10+
from influxdb_logger import InfluxdbLogger
1011
from pms7003 import PMS7003, PMSData
1112

12-
AQI_PM25_BREAKPOINTS = {
13-
14-
}
1513
def get_breakpoint(pm25: float) -> Tuple[str, str]:
1614
"""get colorized breakpoint for the pm25 value"""
1715
if pm25 < 15.5:
@@ -89,6 +87,14 @@ def main(port: str, debug: bool) -> None:
8987
)
9088
return
9189

90+
logger = InfluxdbLogger()
91+
tags = {'type': 'PMS7003', 'id': port}
92+
click.echo(
93+
f"{Fore.BLUE}"
94+
f"writing influxdb measurement {logger.MEASUREMENT} to {logger.LOG_OUTPUT_FILE}"
95+
f"{Style.RESET_ALL}"
96+
)
97+
9298
dev = PMS7003(port)
9399
click.echo(f"{Fore.GREEN}beginning to read data from {port}...{Style.RESET_ALL}")
94100

@@ -98,6 +104,11 @@ def main(port: str, debug: bool) -> None:
98104
print_verbose(data)
99105
else:
100106
print_pm(data)
107+
logger.emit(
108+
fields={k:v for k,v in data._asdict().items() if k.startswith('pm')},
109+
tags=tags,
110+
)
111+
101112
time.sleep(1)
102113

103114
if __name__ == "__main__":

0 commit comments

Comments
 (0)