| //===- Logging_printf.cpp -------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Implementation of the printf logging backend declared in orc-rt-c/Logging.h. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "orc-rt-c/Logging.h" |
| |
| #include "Environment.h" |
| |
| #include <algorithm> |
| #include <cstdarg> |
| #include <cstddef> |
| #include <cstdio> |
| |
| namespace { |
| |
| // The output sink, resolved once on first use: stderr, unless ORC_RT_LOG_OUTPUT |
| // names a file that can be opened for appending. |
| FILE *sink() { |
| static FILE *Sink = []() -> FILE * { |
| const char *Path = orc_rt::secureGetenv("ORC_RT_LOG_OUTPUT"); |
| if (!Path || !*Path) |
| return stderr; |
| FILE *F = std::fopen(Path, "a"); |
| if (!F) { |
| std::fprintf(stderr, |
| "[orc-rt:General:ERROR] could not open ORC_RT_LOG_OUTPUT " |
| "'%s' for logging; using stderr\n", |
| Path); |
| return stderr; |
| } |
| // Line-buffer: every message ends in a newline, so this flushes each |
| // message promptly without the cost of a fully unbuffered stream. |
| std::setvbuf(F, nullptr, _IOLBF, 0); |
| return F; |
| }(); |
| return Sink; |
| } |
| |
| // The runtime level threshold, resolved once on first use from ORC_RT_LOG. A |
| // message is emitted only if its level is at or above this value. When |
| // ORC_RT_LOG is unset the threshold is WARNING: warnings and errors are shown |
| // by default, and info and debug are opt-in. An unrecognized value, or a level |
| // below the compile-time ORC_RT_LOG_LEVEL floor, falls back to the floor, |
| // but never more verbose than INFO. |
| orc_rt_log_Level runtimeLevel() { |
| static orc_rt_log_Level Level = []() -> orc_rt_log_Level { |
| const char *S = orc_rt::secureGetenv("ORC_RT_LOG"); |
| if (!S || !*S) |
| return ORC_RT_LOG_LEVEL_WARNING; |
| orc_rt_log_Level L = orc_rt_log_Level_parse(S); |
| |
| // Fallback for an unavailable or unrecognized request: the compile-time |
| // floor, but never more verbose than INFO. |
| constexpr orc_rt_log_Level Fallback = |
| std::max(ORC_RT_LOG_LEVEL, ORC_RT_LOG_LEVEL_INFO); |
| if (L < 0) { |
| std::fprintf(sink(), |
| "[orc-rt:General:ERROR] ignoring unrecognized ORC_RT_LOG " |
| "value '%s'\n", |
| S); |
| return Fallback; |
| } else if (L < ORC_RT_LOG_LEVEL) { |
| std::fprintf(sink(), |
| "[orc-rt:General:ERROR] requested ORC_RT_LOG = %s is " |
| "unavailable, using lowest available level ORC_RT_LOG = " |
| "%s\n", |
| S, orc_rt_log_Level_getName(Fallback)); |
| return Fallback; |
| } |
| return L; |
| }(); |
| return Level; |
| } |
| |
| constexpr size_t LogBufferSize = 1024; |
| |
| } // namespace |
| |
| extern "C" void orc_rt_log_printf(orc_rt_log_Level Level, |
| orc_rt_log_Category Category, const char *Fmt, |
| ...) noexcept { |
| if (Level < runtimeLevel()) |
| return; |
| |
| char Buf[LogBufferSize]; |
| int P = std::snprintf(Buf, sizeof(Buf), "[orc-rt:%s:%s] ", |
| orc_rt_log_Category_getName(Category), |
| orc_rt_log_Level_getName(Level)); |
| if (P < 0) |
| return; |
| size_t Len = (size_t)P < sizeof(Buf) ? (size_t)P : sizeof(Buf) - 1; |
| |
| va_list Ap; |
| va_start(Ap, Fmt); |
| int B = std::vsnprintf(Buf + Len, sizeof(Buf) - Len, Fmt, Ap); |
| va_end(Ap); |
| if (B > 0) { |
| Len += (size_t)B; |
| if (Len > sizeof(Buf) - 1) |
| Len = sizeof(Buf) - 1; // message was truncated to fit the buffer |
| } |
| |
| // Guarantee a trailing newline, overwriting the last byte when the buffer is |
| // full so that adjacent messages never run together. |
| if (Len == sizeof(Buf) - 1) |
| Buf[Len - 1] = '\n'; |
| else |
| Buf[Len++] = '\n'; |
| |
| // A single stdio call: stdio locks the stream per call, so the whole line is |
| // written atomically with respect to other threads logging to the same sink. |
| std::fwrite(Buf, 1, Len, sink()); |
| } |