Skip to content

Commit

Permalink
Improve prometheus metrics generation reliability
Browse files Browse the repository at this point in the history
That means it shouldn't crash with multiple request types to the same
URI anymore.

Additional changes:
 * Add esptherm_version header containing the current commit hash
 * Add constexpr uint to str utility
 * Add esptherm_build_info metric to metrics page
   * This metrics has the labels esptherm_commit, mcu_type,
arduino_version, sdk_version, and cpp_std_version.
  • Loading branch information
ToMe25 committed Apr 4, 2024
1 parent 0c34afb commit 4614223
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 45 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ wifipass.txt
otapass.txt
mqttuser.txt
mqttpass.txt

# This file changes every commit.
src/generated/esptherm_version.h
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Not all of these are necessarily going to be implemented at all.
* Add MQTT discovery support(https://www.home-assistant.io/docs/mqtt/discovery/)
* Dynamically gzip compress metrics page?
* Implement actual prometheus library as external project
* Add prometheus info metrics esptherm_build_info, esptherm_network_info, esptherm_module_info, and esptherm_sensor_info
* Add prometheus info metrics esptherm_network_info, esptherm_module_info, and esptherm_sensor_info
* Add MQTT metrics to prometheus
* Add prometheus metrics for HTTP response times and sizes
* Add prometheus metrics for push statistics, if DSM is disabled
Expand Down
57 changes: 55 additions & 2 deletions lib/utils/include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,62 @@ constexpr size_t strlen(const char *str) {
#endif

/**
* EXPAND_MACRO is a utility macro to expand a macro to its string form.
* MACRO_TO_STRING is a utility macro to convert a macro to its string form.
*/
#define EXPAND_MACRO(macro) #macro
#define MACRO_TO_STRING(macro) #macro

/**
* EXPAND_MACRO is a utility macro to expand a macro value to its string form.
*/
#define EXPAND_MACRO(macro) MACRO_TO_STRING(macro)

namespace utils {
/**
* A helper struct to convert a set of digits to a C string.
*
* @tparam digits The digits to convert to a string.
*/
template<uint8_t... digits>
struct digits_to_chars { static const char value[]; };

/**
* A helper struct to convert a set of digits to a C string.
*
* @tparam digits The digits to convert to a string.
*/
template<uint8_t... digits>
const char digits_to_chars<digits...>::value[] = {('0' + digits)..., 0};

/**
* A helper struct separating a number into separate digits.
*
* @tparam rem The remainder that is yet to be separated.
* @tparam digits The digits to convert to a string.
*/
template<uint64_t rem, uint8_t... digits>
struct explode : explode<rem / 10, rem % 10, digits...> {};

/**
* A helper struct separating a number into separate digits.
*
* @tparam digits The digits to convert to a string.
*/
template<uint8_t... digits>
struct explode<0, digits...> : digits_to_chars<digits...> {};

/**
* A helper struct converting an unsigned number to a string.
*
* @tparam num The digits to convert to a string.
*/
template<uint64_t num>
struct unsigned_to_string : explode<num / 10, num % 10> {};
} /* namespace utils */

/**
* UNSIGNED_TO_STRING is a helper macro converting an unsigned number to a constexpr string.
*/
#define UNSIGNED_TO_STRING(exp) utils::unsigned_to_string<exp>::value

namespace utils {
/**
Expand Down
4 changes: 3 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ upload_speed = 921600
extra_scripts =
pre:shared/compress_web.py
pre:shared/generate_hash_header.py
pre:shared/generate_version_header.py
lib_deps =
ArduinoOTA
ESPAsyncWebServer = https://github.com/me-no-dev/ESPAsyncWebServer.git
Expand All @@ -52,6 +53,7 @@ board_build.embed_files =
data/gzip/favicon.ico.gz
data/gzip/favicon.png.gz
data/gzip/favicon.svg.gz
; The C++ version is a default for platforms that don't specify one.
build_flags =
-std=c++11
-Wall
Expand Down Expand Up @@ -126,7 +128,7 @@ build_flags =
[env:esp_wroom_02_ota]
extends = env:esp_wroom_02
upload_protocol = espota
upload_port = esp-wifi-Thermometer.local
upload_port = esp-wifi-thermometer.local
extra_scripts =
${env.extra_scripts}
post:shared/read_ota_pass.py
Expand Down
23 changes: 12 additions & 11 deletions shared/generate_hash_header.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,15 @@ def generate_hash_header(hashes: Dict[str, str]) -> None:
"""Generates the header file containing the hashes of the static files.
Generates a header file, in src/generated, containing constant definitions with the hashes of the static files.
@param hashes: The filenames and hashes to generate the header for.
@raise IOError: If opening or writing the header file fails.
"""

print("Generating " + path.relpath(hash_header_path, env.subst("$PROJECT_ROOT"))) # type: ignore[name-defined]

header = open(hash_header_path, 'w')
header.write(
with open(hash_header_path, 'w') as header:
header.write(
"""/*
* web_file_hashes.h
*
Expand All @@ -86,21 +87,21 @@ def generate_hash_header(hashes: Dict[str, str]) -> None:
""")

for file, hash in hashes.items():
header.write(
for file, hash in hashes.items():
header.write(
f"""/**
* The md5 hash of the file "{file}".
*/
""")

id: str = file.upper()
for c in ['.', '-', '/', ' ']:
id = id.replace(c, '_')
id: str = file.upper()
for c in ['.', '-', '/', ' ']:
id = id.replace(c, '_')

header.write(f"static constexpr const char {id}_HASH[] = \"{hash}\";")
header.write(os.linesep + os.linesep)
header.write(f"static constexpr const char {id}_HASH[] = \"{hash}\";")
header.write(os.linesep + os.linesep)

header.write("#endif /* SRC_GENERATED_WEB_FILE_HASHES_H_ */" + os.linesep)
header.write("#endif /* SRC_GENERATED_WEB_FILE_HASHES_H_ */" + os.linesep)


def main() -> int:
Expand Down
105 changes: 105 additions & 0 deletions shared/generate_version_header.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3

import os
from os import path
import subprocess
import sys
from typing import Final
from subprocess import CalledProcessError

try:
Import ("env") # type: ignore[name-defined]
except:
print("Failed to load platformio environment!", file=sys.stderr)
sys.exit(1)

# The path of the header file to generated.
version_header_path: Final[str] = path.join(env.subst('$PROJECT_SRC_DIR'), 'generated', 'esptherm_version.h') # type: ignore[name-defined]


def check_git():
"""Checks whether the git command is available.
Tries to run "git --version" and exits the script if it fails.
"""
try:
subprocess.check_call(['git', '--version'])
except CalledProcessError:
print("Could not execute git. Is there a git executable on the PATH?", file=sys.stderr)
sys.exit(1)

def get_short_hash() -> str:
"""Gets the current commit short-hash.
Runs "git rev-parse --short HEAD" to get the current git commit short-hash.
@return The current commit hash.
@raise CalledProcessError: If the command returns a non-zero exit-code.
"""
return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], text=True).strip()


def generate_version_header(commit: str) -> None:
"""Generates the header file containing the hashes of the static files.
Generates a header file, in src/generated, containing constant definitions with the hashes of the static files.
@raise IOError: If opening or writing the header file fails.
"""

print("Generating " + path.relpath(version_header_path, env.subst("$PROJECT_ROOT"))) # type: ignore[name-defined]

with open(version_header_path, 'w') as header:
header.write(
"""/*
* esptherm_version.h
*
* **Warning:** This file is automatically generated, and should not be edited manually.
*
* This file contains version information about the ESP-WiFi-Thermometer.
* At the moment it only contains the commit hash, but once there are versioned releases it will also contain the version numbers.
*
* This project is licensed under the MIT License.
* The MIT license can be found in the project root and at https://opensource.org/licenses/MIT.
*/
#ifndef SRC_GENERATED_ESPTHERM_VERSION_H_
#define SRC_GENERATED_ESPTHERM_VERSION_H_
""")

header.write(
f"""/**
* The short hash of the ESP-WiFi-Thermometer git commit that was used for this build.
*/
static constexpr const char *ESPTHERM_COMMIT = "{commit}";
""")

header.write("#endif /* SRC_GENERATED_ESPTHERM_VERSION_H_ */" + os.linesep)


def main() -> int:
"""The main entrypoint of this script.
The main function generating the version header file.
@return Zero if nothing goes wrong.
@raise IOError: If opening or reading the file fails.
"""
check_git()
commit: str = ""
try:
commit = get_short_hash()
except CalledProcessError as e:
print(f"Failed to determine git hash. Process failed with exit code {e.returncode}.", file=sys.stderr)
return 1

generate_version_header(commit)

return 0


if __name__ == '__main__' or __name__ == 'SCons.Script':
error: int = main()
if error != 0:
sys.exit(error)
Loading

0 comments on commit 4614223

Please sign in to comment.