//===- 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 #include #include #include 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()); }