/* * Copyright (C) 1996-2024 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #include "squid.h" #include "acl/Gadgets.h" #include "base/TextException.h" #include "cache_cf.h" #include "debug/Stream.h" #include "log/Config.h" #include "log/File.h" #include "log/FormattedLog.h" #include "Parsing.h" #include "sbuf/Stream.h" #include "SquidConfig.h" FormattedLog::~FormattedLog() { close(); // TODO: destructing a Logfile object should be enough aclDestroyAclList(&aclList); safe_free(filename); // leave logFormat alone -- we do not own that object } bool FormattedLog::usesDaemon() const { return (filename && strncmp(filename, "daemon:", 7) == 0); } void FormattedLog::parseOptions(ConfigParser &parser, const char *defaultFormatName) { const char *explicitFormatName = nullptr; char *key = nullptr; char *value = nullptr; while (parser.optionalKvPair(key, value)) { if (strcmp(key, "on-error") == 0) { if (strcmp(value, "die") == 0) { fatal = true; } else if (strcmp(value, "drop") == 0) { fatal = false; } else { throw TextException(ToSBuf("unsupported ", cfg_directive, " on-error value: ", value, Debug::Extra, "expected 'drop' or 'die'"), Here()); } continue; } if (strcmp(key, "buffer-size") == 0) { parseBytesOptionValue(&bufferSize, "bytes", value); continue; } if (strcmp(key, "rotate") == 0) { rotationsToKeep = std::optional(xatoui(value)); continue; } if (strcmp(key, "logformat") == 0 && defaultFormatName) { if (explicitFormatName) throw TextException(ToSBuf("duplicated ", cfg_directive, " option: ", key), Here()); explicitFormatName = value; continue; } throw TextException(ToSBuf("unsupported ", cfg_directive, " option: ", key, "=", value), Here()); } if (const auto formatName = explicitFormatName ? explicitFormatName : defaultFormatName) { assert(defaultFormatName); // this log supports logformat=name setLogformat(formatName); } // else OK: this log does not support logformat=name and none was given } void FormattedLog::dumpOptions(std::ostream &os) const { /* do not report defaults */ // TODO: Here and elsewhere, report both explicitly configured settings and // various defaults. Properly excluding defaults requires wrapping most // non-pointer members in std::optional and adding methods to compute the final // option value after accounting for defaults (and those may change with // reconfiguration!). And all that effort may still not result in a faithful // reproduction of the original squid.conf because of size unit changes, // order changes, duplicates removal, etc. More importantly, these reports // are much more useful for determining complete Squid state (especially // when triaging older Squids with some difficult-to-figure-out defaults). switch (type) { case Log::Format::CLF_UNKNOWN: break; // do not report a format when it was not configured case Log::Format::CLF_NONE: break; // the special "none" case has no format to report case Log::Format::CLF_SQUID: break; // do not report default format (XXX: icap_log default differs) case Log::Format::CLF_CUSTOM: if (logFormat) // paranoid; the format should be set os << " logformat=" << logFormat->name; break; default: os << " logformat=" << Log::LogConfig::BuiltInFormatName(type); } if (!fatal) os << " on-error=drop"; if (bufferSize != 8*MAX_URL) os << " buffer-size=" << bufferSize << "bytes"; if (rotationsToKeep) os << " rotate=" << rotationsToKeep.value(); } void FormattedLog::setLogformat(const char *logformatName) { assert(logformatName); assert(type == Log::Format::CLF_UNKNOWN); // set only once assert(!logFormat); // set only once debugs(3, 7, "possible " << filename << " logformat: " << logformatName); if (const auto lf = Log::TheConfig.findCustomFormat(logformatName)) { type = Log::Format::CLF_CUSTOM; logFormat = lf; return; } if (const auto id = Log::LogConfig::FindBuiltInFormat(logformatName)) { type = id; return; } throw TextException(ToSBuf("unknown logformat name in ", cfg_directive, ": ", logformatName), Here()); } void FormattedLog::open() { Must(!logfile); Must(filename); logfile = logfileOpen(filename, bufferSize, fatal); // the opening code reports failures and returns nil if they are non-fatal } void FormattedLog::rotate() { if (logfile) logfileRotate(logfile, rotationsToKeep.value_or(Config.Log.rotateNumber)); } void FormattedLog::close() { if (logfile) { logfileClose(logfile); logfile = nullptr; // deleted by the closing code } }