mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-22 22:58:33 +00:00
Add and test QPDFLogger class
This commit is contained in:
parent
f588d74140
commit
f1f711963b
158
include/qpdf/QPDFLogger.hh
Normal file
158
include/qpdf/QPDFLogger.hh
Normal file
@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2005-2022 Jay Berkenbilt
|
||||
//
|
||||
// This file is part of qpdf.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Versions of qpdf prior to version 7 were released under the terms
|
||||
// of version 2.0 of the Artistic License. At your option, you may
|
||||
// continue to consider qpdf to be licensed under those terms. Please
|
||||
// see the manual for additional information.
|
||||
|
||||
#ifndef QPDFLOGGER_HH
|
||||
#define QPDFLOGGER_HH
|
||||
|
||||
#include <qpdf/DLL.h>
|
||||
#include <qpdf/Pipeline.hh>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
class QPDFLogger
|
||||
{
|
||||
public:
|
||||
QPDF_DLL
|
||||
QPDFLogger();
|
||||
|
||||
QPDF_DLL
|
||||
static std::shared_ptr<QPDFLogger> defaultLogger();
|
||||
|
||||
// Defaults:
|
||||
//
|
||||
// info -- if save is standard output, standard error, else standard output
|
||||
// warn -- whatever error points to
|
||||
// error -- standard error
|
||||
// save -- undefined unless set
|
||||
//
|
||||
// On deletion, finish() is called for the standard output and
|
||||
// standard error pipelines, which flushes output. If you supply
|
||||
// any custom pipelines, you must call finish() on them yourself.
|
||||
// Note that calling finish is not needed for string, stdio, or
|
||||
// ostream pipelines.
|
||||
//
|
||||
// NOTES ABOUT THE SAVE PIPELINE
|
||||
//
|
||||
// You should never set the save pipeline to the same destination
|
||||
// as something else. Doing so will corrupt your save output. If
|
||||
// you want to save to standard output, use the method
|
||||
// saveToStandardOutput(). In addition to setting the save
|
||||
// pipeline, that does the following extra things:
|
||||
//
|
||||
// * If standard output has been used, a logic error is thrown
|
||||
// * If info is set to standard output at the time of the set save
|
||||
// call, it is switched to standard error.
|
||||
//
|
||||
// This is not a guarantee. You can still mess this up in ways
|
||||
// that are not checked. Here are a few examples:
|
||||
//
|
||||
// * Don't set any pipeline to standard output *after* passing it
|
||||
// to setSave()
|
||||
// * Don't use a separate mechanism to write stdout/stderr other
|
||||
// than QPDFLogger::standardOutput()
|
||||
// * Don't set anything to the same custom pipeline that save is
|
||||
// set to.
|
||||
//
|
||||
// Just be sure that if you change pipelines around, you should
|
||||
// avoid having the save pipeline also be used for any other
|
||||
// purpose. The special case for saving to standard output allows
|
||||
// you to call saveToStandardOutput() early without having to
|
||||
// worry about the info pipeline.
|
||||
|
||||
QPDF_DLL
|
||||
void info(char const*);
|
||||
QPDF_DLL
|
||||
void info(std::string const&);
|
||||
QPDF_DLL
|
||||
std::shared_ptr<Pipeline> getInfo(bool null_okay = false);
|
||||
|
||||
QPDF_DLL
|
||||
void warn(char const*);
|
||||
QPDF_DLL
|
||||
void warn(std::string const&);
|
||||
QPDF_DLL
|
||||
std::shared_ptr<Pipeline> getWarn(bool null_okay = false);
|
||||
|
||||
QPDF_DLL
|
||||
void error(char const*);
|
||||
QPDF_DLL
|
||||
void error(std::string const&);
|
||||
QPDF_DLL
|
||||
std::shared_ptr<Pipeline> getError(bool null_okay = false);
|
||||
|
||||
QPDF_DLL
|
||||
std::shared_ptr<Pipeline> getSave(bool null_okay = false);
|
||||
|
||||
QPDF_DLL
|
||||
std::shared_ptr<Pipeline> standardOutput();
|
||||
QPDF_DLL
|
||||
std::shared_ptr<Pipeline> standardError();
|
||||
QPDF_DLL
|
||||
std::shared_ptr<Pipeline> discard();
|
||||
|
||||
// Passing a null pointer resets to default
|
||||
QPDF_DLL
|
||||
void setInfo(std::shared_ptr<Pipeline>);
|
||||
QPDF_DLL
|
||||
void setWarn(std::shared_ptr<Pipeline>);
|
||||
QPDF_DLL
|
||||
void setError(std::shared_ptr<Pipeline>);
|
||||
// See notes above about the save pipeline
|
||||
QPDF_DLL
|
||||
void setSave(std::shared_ptr<Pipeline>);
|
||||
QPDF_DLL
|
||||
void saveToStandardOutput();
|
||||
|
||||
// Shortcut for logic to reset output to new output/error streams.
|
||||
// out_stream is used for info, err_stream is used for error, and
|
||||
// warning is cleared so that it follows error.
|
||||
QPDF_DLL
|
||||
void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pipeline>
|
||||
throwIfNull(std::shared_ptr<Pipeline>, bool null_okay);
|
||||
|
||||
class Members
|
||||
{
|
||||
friend class QPDFLogger;
|
||||
|
||||
public:
|
||||
QPDF_DLL
|
||||
~Members();
|
||||
|
||||
private:
|
||||
Members();
|
||||
Members(Members const&) = delete;
|
||||
|
||||
std::shared_ptr<Pipeline> p_discard;
|
||||
std::shared_ptr<Pipeline> p_real_stdout;
|
||||
std::shared_ptr<Pipeline> p_stdout;
|
||||
std::shared_ptr<Pipeline> p_stderr;
|
||||
std::shared_ptr<Pipeline> p_info;
|
||||
std::shared_ptr<Pipeline> p_warn;
|
||||
std::shared_ptr<Pipeline> p_error;
|
||||
std::shared_ptr<Pipeline> p_save;
|
||||
};
|
||||
std::shared_ptr<Members> m;
|
||||
};
|
||||
|
||||
#endif // QPDFLOGGER_HH
|
@ -67,6 +67,7 @@ set(libqpdf_SOURCES
|
||||
QPDFJob_argv.cc
|
||||
QPDFJob_config.cc
|
||||
QPDFJob_json.cc
|
||||
QPDFLogger.cc
|
||||
QPDFMatrix.cc
|
||||
QPDFNameTreeObjectHelper.cc
|
||||
QPDFNumberTreeObjectHelper.cc
|
||||
|
244
libqpdf/QPDFLogger.cc
Normal file
244
libqpdf/QPDFLogger.cc
Normal file
@ -0,0 +1,244 @@
|
||||
#include <qpdf/QPDFLogger.hh>
|
||||
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/Pl_OStream.hh>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace
|
||||
{
|
||||
class Pl_Track: public Pipeline
|
||||
{
|
||||
public:
|
||||
Pl_Track(char const* identifier, Pipeline* next) :
|
||||
Pipeline(identifier, next),
|
||||
used(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
write(unsigned char const* data, size_t len) override
|
||||
{
|
||||
this->used = true;
|
||||
getNext()->write(data, len);
|
||||
}
|
||||
|
||||
virtual void
|
||||
finish() override
|
||||
{
|
||||
getNext()->finish();
|
||||
}
|
||||
|
||||
bool
|
||||
getUsed() const
|
||||
{
|
||||
return used;
|
||||
}
|
||||
|
||||
private:
|
||||
bool used;
|
||||
};
|
||||
}; // namespace
|
||||
|
||||
QPDFLogger::Members::Members() :
|
||||
p_discard(new Pl_Discard()),
|
||||
p_real_stdout(new Pl_OStream("standard output", std::cout)),
|
||||
p_stdout(new Pl_Track("track stdout", p_real_stdout.get())),
|
||||
p_stderr(new Pl_OStream("standard error", std::cerr)),
|
||||
p_info(p_stdout),
|
||||
p_warn(nullptr),
|
||||
p_error(p_stderr),
|
||||
p_save(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
QPDFLogger::Members::~Members()
|
||||
{
|
||||
p_stdout->finish();
|
||||
p_stderr->finish();
|
||||
}
|
||||
|
||||
QPDFLogger::QPDFLogger() :
|
||||
m(new Members())
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<QPDFLogger>
|
||||
QPDFLogger::defaultLogger()
|
||||
{
|
||||
static auto l = std::make_shared<QPDFLogger>();
|
||||
return l;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::info(char const* s)
|
||||
{
|
||||
getInfo(false)->writeCStr(s);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::info(std::string const& s)
|
||||
{
|
||||
getInfo(false)->writeString(s);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::getInfo(bool null_okay)
|
||||
{
|
||||
return throwIfNull(this->m->p_info, null_okay);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::warn(char const* s)
|
||||
{
|
||||
getWarn(false)->writeCStr(s);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::warn(std::string const& s)
|
||||
{
|
||||
getWarn(false)->writeString(s);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::getWarn(bool null_okay)
|
||||
{
|
||||
if (this->m->p_warn) {
|
||||
return this->m->p_warn;
|
||||
}
|
||||
return getError(null_okay);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::error(char const* s)
|
||||
{
|
||||
getError(false)->writeCStr(s);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::error(std::string const& s)
|
||||
{
|
||||
getError(false)->writeString(s);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::getError(bool null_okay)
|
||||
{
|
||||
return throwIfNull(this->m->p_error, null_okay);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::getSave(bool null_okay)
|
||||
{
|
||||
return throwIfNull(this->m->p_save, null_okay);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::standardOutput()
|
||||
{
|
||||
return this->m->p_stdout;
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::standardError()
|
||||
{
|
||||
return this->m->p_stderr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::discard()
|
||||
{
|
||||
return this->m->p_discard;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::setInfo(std::shared_ptr<Pipeline> p)
|
||||
{
|
||||
if (p == nullptr) {
|
||||
if (this->m->p_save == this->m->p_stdout) {
|
||||
p = this->m->p_stderr;
|
||||
} else {
|
||||
p = this->m->p_stdout;
|
||||
}
|
||||
}
|
||||
this->m->p_info = p;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::setWarn(std::shared_ptr<Pipeline> p)
|
||||
{
|
||||
this->m->p_warn = p;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::setError(std::shared_ptr<Pipeline> p)
|
||||
{
|
||||
if (p == nullptr) {
|
||||
p = this->m->p_stderr;
|
||||
}
|
||||
this->m->p_error = p;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::setSave(std::shared_ptr<Pipeline> p)
|
||||
{
|
||||
if (p == this->m->p_stdout) {
|
||||
auto pt = dynamic_cast<Pl_Track*>(p.get());
|
||||
if (pt->getUsed()) {
|
||||
throw std::logic_error(
|
||||
"QPDFLogger: called setSave on standard output after standard"
|
||||
" output has already been used");
|
||||
}
|
||||
if (this->m->p_info == this->m->p_stdout) {
|
||||
this->m->p_info = this->m->p_stderr;
|
||||
}
|
||||
}
|
||||
this->m->p_save = p;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::saveToStandardOutput()
|
||||
{
|
||||
setSave(standardOutput());
|
||||
}
|
||||
|
||||
void
|
||||
QPDFLogger::setOutputStreams(std::ostream* out_stream, std::ostream* err_stream)
|
||||
{
|
||||
if (out_stream == &std::cout) {
|
||||
out_stream = nullptr;
|
||||
}
|
||||
if (err_stream == &std::cerr) {
|
||||
err_stream = nullptr;
|
||||
}
|
||||
std::shared_ptr<Pipeline> new_out;
|
||||
std::shared_ptr<Pipeline> new_err;
|
||||
|
||||
if (out_stream == nullptr) {
|
||||
if (this->m->p_save == this->m->p_stdout) {
|
||||
new_out = this->m->p_stderr;
|
||||
} else {
|
||||
new_out = this->m->p_stdout;
|
||||
}
|
||||
} else {
|
||||
new_out = std::make_shared<Pl_OStream>("output", *out_stream);
|
||||
}
|
||||
if (err_stream == nullptr) {
|
||||
new_err = this->m->p_stderr;
|
||||
} else {
|
||||
new_err = std::make_shared<Pl_OStream>("error output", *err_stream);
|
||||
}
|
||||
this->m->p_info = new_out;
|
||||
this->m->p_warn = nullptr;
|
||||
this->m->p_error = new_err;
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline>
|
||||
QPDFLogger::throwIfNull(std::shared_ptr<Pipeline> p, bool null_okay)
|
||||
{
|
||||
if (!(null_okay || p)) {
|
||||
throw std::logic_error(
|
||||
"QPDFLogger: requested a null pipeline without null_okay == true");
|
||||
}
|
||||
return p;
|
||||
}
|
@ -16,6 +16,7 @@ set(TEST_PROGRAMS
|
||||
json
|
||||
json_handler
|
||||
json_parse
|
||||
logger
|
||||
lzw
|
||||
main_from_wmain
|
||||
matrix
|
||||
|
109
libtests/logger.cc
Normal file
109
libtests/logger.cc
Normal file
@ -0,0 +1,109 @@
|
||||
#include <qpdf/assert_test.h>
|
||||
|
||||
#include <qpdf/Pl_String.hh>
|
||||
#include <qpdf/QPDFLogger.hh>
|
||||
#include <stdexcept>
|
||||
|
||||
static void
|
||||
test1()
|
||||
{
|
||||
// Standard behavior
|
||||
|
||||
auto logger = QPDFLogger::defaultLogger();
|
||||
|
||||
logger->info("info to stdout\n");
|
||||
logger->warn("warn to stderr\n");
|
||||
logger->error("error to stderr\n");
|
||||
assert(logger->getSave(true) == nullptr);
|
||||
try {
|
||||
logger->getSave();
|
||||
assert(false);
|
||||
} catch (std::logic_error& e) {
|
||||
*(logger->getInfo()) << "getSave exception: " << e.what() << "\n";
|
||||
}
|
||||
try {
|
||||
logger->saveToStandardOutput();
|
||||
assert(false);
|
||||
} catch (std::logic_error& e) {
|
||||
*(logger->getInfo())
|
||||
<< "saveToStandardOutput exception: " << e.what() << "\n";
|
||||
}
|
||||
logger->setWarn(logger->discard());
|
||||
logger->warn("warning not seen\n");
|
||||
logger->setWarn(nullptr);
|
||||
logger->warn("restored warning to stderr\n");
|
||||
}
|
||||
|
||||
static void
|
||||
test2()
|
||||
{
|
||||
// First call saveToStandardOutput. Then use info, which then to
|
||||
// go stderr.
|
||||
QPDFLogger l;
|
||||
l.saveToStandardOutput();
|
||||
l.info(std::string("info to stderr\n"));
|
||||
*(l.getSave()) << "save to stdout\n";
|
||||
l.setInfo(nullptr);
|
||||
l.info("info still to stderr\n");
|
||||
l.setSave(nullptr);
|
||||
l.setInfo(nullptr);
|
||||
l.info("info back to stdout\n");
|
||||
}
|
||||
|
||||
static void
|
||||
test3()
|
||||
{
|
||||
// Error/warning
|
||||
QPDFLogger l;
|
||||
|
||||
// Warning follows error when error is set explicitly.
|
||||
std::string errors;
|
||||
auto pl_error = std::make_shared<Pl_String>("errors", nullptr, errors);
|
||||
l.setError(pl_error);
|
||||
l.warn("warn follows error\n");
|
||||
assert(errors == "warn follows error\n");
|
||||
l.error("error too\n");
|
||||
assert(errors == "warn follows error\nerror too\n");
|
||||
|
||||
// Set warnings -- now they're separate
|
||||
std::string warnings;
|
||||
auto pl_warn = std::make_shared<Pl_String>("warnings", nullptr, warnings);
|
||||
l.setWarn(pl_warn);
|
||||
l.warn(std::string("warning now separate\n"));
|
||||
l.error(std::string("new error\n"));
|
||||
assert(warnings == "warning now separate\n");
|
||||
assert(errors == "warn follows error\nerror too\nnew error\n");
|
||||
std::string errors2;
|
||||
pl_error = std::make_shared<Pl_String>("errors", nullptr, errors2);
|
||||
l.setError(pl_error);
|
||||
l.warn("new warning\n");
|
||||
l.error("another new error\n");
|
||||
assert(warnings == "warning now separate\nnew warning\n");
|
||||
assert(errors == "warn follows error\nerror too\nnew error\n");
|
||||
assert(errors2 == "another new error\n");
|
||||
|
||||
// Restore warnings to default -- follows error again
|
||||
l.setWarn(nullptr);
|
||||
l.warn("warning 3\n");
|
||||
l.error("error 3\n");
|
||||
assert(warnings == "warning now separate\nnew warning\n");
|
||||
assert(errors == "warn follows error\nerror too\nnew error\n");
|
||||
assert(errors2 == "another new error\nwarning 3\nerror 3\n");
|
||||
|
||||
// Restore everything to default
|
||||
l.setInfo(nullptr);
|
||||
l.setWarn(nullptr);
|
||||
l.setError(nullptr);
|
||||
l.info("after reset, info to stdout\n");
|
||||
l.warn("after reset, warn to stderr\n");
|
||||
l.error("after reset, error to stderr\n");
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
return 0;
|
||||
}
|
33
libtests/qtest/logger.test
Normal file
33
libtests/qtest/logger.test
Normal file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env perl
|
||||
require 5.008;
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
chdir("logger") or die "chdir testdir failed: $!\n";
|
||||
|
||||
require TestDriver;
|
||||
|
||||
my $td = new TestDriver('logger');
|
||||
|
||||
cleanup();
|
||||
|
||||
$td->runtest("logger",
|
||||
{$td->COMMAND => "logger >stdout 2>stderr"},
|
||||
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("check stdout",
|
||||
{$td->FILE => "stdout"},
|
||||
{$td->FILE => "exp-stdout"},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("check stderr",
|
||||
{$td->FILE => "stderr"},
|
||||
{$td->FILE => "exp-stderr"},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
cleanup();
|
||||
$td->report(3);
|
||||
|
||||
sub cleanup
|
||||
{
|
||||
unlink "stdout", "stderr";
|
||||
}
|
7
libtests/qtest/logger/exp-stderr
Normal file
7
libtests/qtest/logger/exp-stderr
Normal file
@ -0,0 +1,7 @@
|
||||
warn to stderr
|
||||
error to stderr
|
||||
restored warning to stderr
|
||||
info to stderr
|
||||
info still to stderr
|
||||
after reset, warn to stderr
|
||||
after reset, error to stderr
|
6
libtests/qtest/logger/exp-stdout
Normal file
6
libtests/qtest/logger/exp-stdout
Normal file
@ -0,0 +1,6 @@
|
||||
info to stdout
|
||||
getSave exception: QPDFLogger: requested a null pipeline without null_okay == true
|
||||
saveToStandardOutput exception: QPDFLogger: called setSave on standard output after standard output has already been used
|
||||
save to stdout
|
||||
info back to stdout
|
||||
after reset, info to stdout
|
@ -31,6 +31,7 @@
|
||||
#include <qpdf/QPDFFileSpecObjectHelper.hh>
|
||||
#include <qpdf/QPDFFormFieldObjectHelper.hh>
|
||||
#include <qpdf/QPDFJob.hh>
|
||||
#include <qpdf/QPDFLogger.hh>
|
||||
#include <qpdf/QPDFMatrix.hh>
|
||||
#include <qpdf/QPDFNameTreeObjectHelper.hh>
|
||||
#include <qpdf/QPDFNumberTreeObjectHelper.hh>
|
||||
@ -98,6 +99,7 @@ main()
|
||||
print_size(QPDFJob::EncConfig);
|
||||
print_size(QPDFJob::PagesConfig);
|
||||
print_size(QPDFJob::UOConfig);
|
||||
print_size(QPDFLogger);
|
||||
print_size(QPDFMatrix);
|
||||
print_size(QPDFNameTreeObjectHelper);
|
||||
print_size(QPDFNameTreeObjectHelper::iterator);
|
||||
|
Loading…
x
Reference in New Issue
Block a user