passenger 5.1.7 → 5.1.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG +13 -2
- data/CONTRIBUTING.md +1 -1
- data/build/agent.rb +1 -1
- data/build/cxx_tests.rb +6 -0
- data/build/support/cxx_dependency_map.rb +1286 -391
- data/build/support/general.rb +0 -26
- data/resources/templates/standalone/rails_asset_pipeline.erb +2 -2
- data/src/agent/Core/ApiServer.h +49 -44
- data/src/agent/Core/ApplicationPool/Pool.h +1 -1
- data/src/agent/Core/ApplicationPool/Process.h +1 -1
- data/src/agent/Core/ApplicationPool/Socket.h +1 -1
- data/src/agent/Core/Controller.h +16 -8
- data/src/agent/Core/Controller/CheckoutSession.cpp +1 -1
- data/src/agent/Core/Controller/Config.cpp +68 -0
- data/src/agent/Core/Controller/Config.h +70 -34
- data/src/agent/Core/Controller/ForwardResponse.cpp +5 -5
- data/src/agent/Core/Controller/Hooks.cpp +5 -14
- data/src/agent/Core/Controller/Implementation.cpp +1 -1
- data/src/agent/Core/Controller/InitRequest.cpp +31 -29
- data/src/agent/Core/Controller/InitializationAndShutdown.cpp +4 -4
- data/src/agent/Core/Controller/InternalUtils.cpp +3 -3
- data/src/agent/Core/Controller/Miscellaneous.cpp +1 -1
- data/src/agent/Core/Controller/Request.h +2 -2
- data/src/agent/Core/Controller/SendRequest.cpp +5 -5
- data/src/agent/Core/Controller/StateInspection.cpp +1 -1
- data/src/agent/Core/Controller/TurboCaching.h +2 -2
- data/src/agent/Core/CoreMain.cpp +2 -2
- data/src/agent/Core/ResponseCache.h +3 -3
- data/src/agent/Core/SpawningKit/BackgroundIOCapturer.h +3 -3
- data/src/agent/Core/SpawningKit/DirectSpawner.h +2 -2
- data/src/agent/Core/SpawningKit/PipeWatcher.h +3 -3
- data/src/agent/Core/SpawningKit/SmartSpawner.h +2 -2
- data/src/agent/Core/SpawningKit/Spawner.h +1 -1
- data/src/agent/Core/UnionStation/Connection.h +1 -1
- data/src/agent/Core/UnionStation/Context.h +1 -1
- data/src/agent/Core/UnionStation/Transaction.h +1 -1
- data/src/agent/Shared/ApiServerUtils.h +73 -27
- data/src/agent/Shared/Base.cpp +61 -73
- data/src/agent/UstRouter/ApiServer.h +34 -45
- data/src/agent/UstRouter/Controller.h +86 -60
- data/src/agent/UstRouter/RemoteSender.h +1 -1
- data/src/agent/UstRouter/RemoteSink.h +1 -1
- data/src/agent/Watchdog/ApiServer.h +42 -50
- data/src/agent/Watchdog/WatchdogMain.cpp +1 -1
- data/src/apache2_module/Configuration.hpp +1 -1
- data/src/apache2_module/Hooks.cpp +27 -13
- data/src/cxx_supportlib/AppTypes.h +1 -1
- data/src/cxx_supportlib/BackgroundEventLoop.cpp +1 -1
- data/src/cxx_supportlib/ConfigKit/AsyncUtils.h +86 -0
- data/src/cxx_supportlib/ConfigKit/Common.h +6 -3
- data/src/cxx_supportlib/ConfigKit/IN_PRACTICE.md +1039 -0
- data/src/cxx_supportlib/ConfigKit/README.md +112 -497
- data/src/cxx_supportlib/ConfigKit/Schema.h +78 -15
- data/src/cxx_supportlib/ConfigKit/Store.h +272 -53
- data/src/cxx_supportlib/ConfigKit/SubComponentUtils.h +59 -0
- data/src/cxx_supportlib/ConfigKit/Utils.h +26 -65
- data/src/cxx_supportlib/ConfigKit/ValidationUtils.h +69 -0
- data/src/cxx_supportlib/ConfigKit/VariantMapUtils.h +7 -4
- data/src/cxx_supportlib/Constants.h +4 -1
- data/src/cxx_supportlib/Crypto.cpp +1 -1
- data/src/cxx_supportlib/DataStructures/StringKeyTable.h +26 -7
- data/src/cxx_supportlib/FileDescriptor.h +1 -1
- data/src/cxx_supportlib/Hooks.h +1 -1
- data/src/cxx_supportlib/LoggingKit/Assert.h +130 -0
- data/src/cxx_supportlib/LoggingKit/Config.h +97 -0
- data/src/cxx_supportlib/LoggingKit/Context.h +94 -0
- data/src/cxx_supportlib/LoggingKit/Forward.h +95 -0
- data/src/cxx_supportlib/LoggingKit/Implementation.cpp +695 -0
- data/src/cxx_supportlib/LoggingKit/Logging.h +204 -0
- data/src/cxx_supportlib/LoggingKit/LoggingKit.h +33 -0
- data/src/cxx_supportlib/LveLoggingDecorator.h +1 -1
- data/src/cxx_supportlib/MemoryKit/mbuf.cpp +1 -1
- data/src/cxx_supportlib/RandomGenerator.h +1 -1
- data/src/cxx_supportlib/SafeLibev.h +1 -1
- data/src/cxx_supportlib/ServerKit/AcceptLoadBalancer.h +1 -1
- data/src/cxx_supportlib/ServerKit/Channel.h +1 -1
- data/src/cxx_supportlib/ServerKit/FileBufferedChannel.h +1 -1
- data/src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h +1 -1
- data/src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h +1 -1
- data/src/cxx_supportlib/ServerKit/HttpHeaderParser.h +1 -1
- data/src/cxx_supportlib/ServerKit/HttpServer.h +48 -15
- data/src/cxx_supportlib/ServerKit/Server.h +79 -52
- data/src/cxx_supportlib/StaticString.h +12 -0
- data/src/cxx_supportlib/Utils/Curl.h +16 -0
- data/src/cxx_supportlib/Utils/FastStringStream.h +6 -1
- data/src/cxx_supportlib/Utils/ScopeGuard.h +1 -1
- data/src/cxx_supportlib/Utils/StrIntUtils.cpp +2 -19
- data/src/cxx_supportlib/WatchdogLauncher.h +3 -2
- data/src/ruby_supportlib/phusion_passenger.rb +3 -3
- data/src/ruby_supportlib/phusion_passenger/common_library.rb +12 -12
- data/src/ruby_supportlib/phusion_passenger/constants.rb +6 -3
- data/src/ruby_supportlib/phusion_passenger/standalone/start_command.rb +1 -0
- data/src/ruby_supportlib/phusion_passenger/standalone/stop_command.rb +1 -0
- metadata +14 -4
- data/src/cxx_supportlib/Logging.cpp +0 -295
- data/src/cxx_supportlib/Logging.h +0 -385
@@ -0,0 +1,97 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2017 Phusion Holding B.V.
|
4
|
+
*
|
5
|
+
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
|
+
* trademarks of Phusion Holding B.V.
|
7
|
+
*
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
13
|
+
* furnished to do so, subject to the following conditions:
|
14
|
+
*
|
15
|
+
* The above copyright notice and this permission notice shall be included in
|
16
|
+
* all copies or substantial portions of the Software.
|
17
|
+
*
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
* THE SOFTWARE.
|
25
|
+
*/
|
26
|
+
#ifndef _PASSENGER_LOGGING_KIT_CONFIG_H_
|
27
|
+
#define _PASSENGER_LOGGING_KIT_CONFIG_H_
|
28
|
+
|
29
|
+
#include <boost/config.hpp>
|
30
|
+
#include <boost/scoped_ptr.hpp>
|
31
|
+
#include <boost/noncopyable.hpp>
|
32
|
+
|
33
|
+
#include <string>
|
34
|
+
#include <vector>
|
35
|
+
|
36
|
+
#include <LoggingKit/Forward.h>
|
37
|
+
#include <ConfigKit/Schema.h>
|
38
|
+
|
39
|
+
#include <jsoncpp/json.h>
|
40
|
+
|
41
|
+
namespace Passenger {
|
42
|
+
namespace LoggingKit {
|
43
|
+
|
44
|
+
using namespace std;
|
45
|
+
|
46
|
+
|
47
|
+
class Schema: public ConfigKit::Schema {
|
48
|
+
private:
|
49
|
+
static Json::Value createStderrTarget();
|
50
|
+
static void validateLogLevel(const string &key, const ConfigKit::Store &store,
|
51
|
+
vector<ConfigKit::Error> &errors);
|
52
|
+
static void validateTarget(const string &key, const ConfigKit::Store &store,
|
53
|
+
vector<ConfigKit::Error> &errors);
|
54
|
+
|
55
|
+
public:
|
56
|
+
Schema();
|
57
|
+
};
|
58
|
+
|
59
|
+
struct ConfigRealization {
|
60
|
+
enum FdClosePolicy {
|
61
|
+
NEVER_CLOSE,
|
62
|
+
ALWAYS_CLOSE,
|
63
|
+
CLOSE_WHEN_FINALIZED
|
64
|
+
};
|
65
|
+
|
66
|
+
Level level;
|
67
|
+
Level appOutputLogLevel;
|
68
|
+
|
69
|
+
TargetType targetType;
|
70
|
+
TargetType fileDescriptorLogTargetType;
|
71
|
+
int targetFd;
|
72
|
+
int fileDescriptorLogTargetFd;
|
73
|
+
FdClosePolicy targetFdClosePolicy;
|
74
|
+
FdClosePolicy fileDescriptorLogTargetFdClosePolicy;
|
75
|
+
bool finalized;
|
76
|
+
|
77
|
+
ConfigRealization(const ConfigKit::Store &store);
|
78
|
+
~ConfigRealization();
|
79
|
+
|
80
|
+
void apply(const ConfigKit::Store &config, ConfigRealization *oldConfigRlz)
|
81
|
+
BOOST_NOEXCEPT_OR_NOTHROW;
|
82
|
+
void finalize();
|
83
|
+
};
|
84
|
+
|
85
|
+
struct ConfigChangeRequest: public boost::noncopyable {
|
86
|
+
boost::scoped_ptr<ConfigKit::Store> config;
|
87
|
+
ConfigRealization *configRlz;
|
88
|
+
|
89
|
+
ConfigChangeRequest();
|
90
|
+
~ConfigChangeRequest();
|
91
|
+
};
|
92
|
+
|
93
|
+
|
94
|
+
} // namespace LoggingKit
|
95
|
+
} // namespace Passenger
|
96
|
+
|
97
|
+
#endif /* _PASSENGER_LOGGING_KIT_CONFIG_H_ */
|
@@ -0,0 +1,94 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2017 Phusion Holding B.V.
|
4
|
+
*
|
5
|
+
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
|
+
* trademarks of Phusion Holding B.V.
|
7
|
+
*
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
13
|
+
* furnished to do so, subject to the following conditions:
|
14
|
+
*
|
15
|
+
* The above copyright notice and this permission notice shall be included in
|
16
|
+
* all copies or substantial portions of the Software.
|
17
|
+
*
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
* THE SOFTWARE.
|
25
|
+
*/
|
26
|
+
#ifndef _PASSENGER_LOGGING_KIT_CONTEXT_H_
|
27
|
+
#define _PASSENGER_LOGGING_KIT_CONTEXT_H_
|
28
|
+
|
29
|
+
#include <oxt/macros.hpp>
|
30
|
+
#include <boost/thread.hpp>
|
31
|
+
#include <boost/atomic.hpp>
|
32
|
+
#include <ConfigKit/ConfigKit.h>
|
33
|
+
#include <LoggingKit/Forward.h>
|
34
|
+
#include <LoggingKit/Config.h>
|
35
|
+
|
36
|
+
namespace Passenger {
|
37
|
+
namespace LoggingKit {
|
38
|
+
|
39
|
+
using namespace std;
|
40
|
+
using namespace oxt;
|
41
|
+
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Note about file descriptor handling:
|
45
|
+
* the "target" and "file_descriptor_log_target" config options
|
46
|
+
* accept an "fd" suboption to force LoggingKit to use a specific
|
47
|
+
* file descriptor. LoggingKit will take over ownership of this fd,
|
48
|
+
* but only in the following circumstances:
|
49
|
+
*
|
50
|
+
* - If you pass this "fd" option to the Context constructor, then
|
51
|
+
* LoggingKit takes ownership only when the constructor
|
52
|
+
* succeeds.
|
53
|
+
* - If you pass this "fd" option to `prepareConfigChange()`, then
|
54
|
+
* LoggingKit takes ownership only when `commitConfigChange()`
|
55
|
+
* returns.
|
56
|
+
*
|
57
|
+
* If anything goes wrong in the constructor, or if
|
58
|
+
* `commitConfigChange()` is never called, then the caller is
|
59
|
+
* responsible for cleaning up the fd.
|
60
|
+
*/
|
61
|
+
class Context {
|
62
|
+
public:
|
63
|
+
typedef LoggingKit::ConfigChangeRequest ConfigChangeRequest;
|
64
|
+
|
65
|
+
private:
|
66
|
+
Schema schema;
|
67
|
+
mutable boost::mutex syncher;
|
68
|
+
ConfigKit::Store config;
|
69
|
+
boost::atomic<ConfigRealization *> configRlz;
|
70
|
+
|
71
|
+
public:
|
72
|
+
Context(const Json::Value &initialConfig = Json::Value());
|
73
|
+
ConfigKit::Store getConfig() const;
|
74
|
+
|
75
|
+
bool prepareConfigChange(const Json::Value &updates,
|
76
|
+
vector<ConfigKit::Error> &errors,
|
77
|
+
LoggingKit::ConfigChangeRequest &req);
|
78
|
+
void commitConfigChange(LoggingKit::ConfigChangeRequest &req)
|
79
|
+
BOOST_NOEXCEPT_OR_NOTHROW;
|
80
|
+
|
81
|
+
OXT_FORCE_INLINE
|
82
|
+
const ConfigRealization *getConfigRealization() const {
|
83
|
+
return configRlz.load(boost::memory_order_acquire);
|
84
|
+
}
|
85
|
+
};
|
86
|
+
|
87
|
+
|
88
|
+
void initialize(const Json::Value &initialConfig);
|
89
|
+
|
90
|
+
|
91
|
+
} // namespace LoggingKit
|
92
|
+
} // namespace Passenger
|
93
|
+
|
94
|
+
#endif /* _PASSENGER_LOGGING_KIT_CONTEXT_H_ */
|
@@ -0,0 +1,95 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2017 Phusion Holding B.V.
|
4
|
+
*
|
5
|
+
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
|
+
* trademarks of Phusion Holding B.V.
|
7
|
+
*
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
13
|
+
* furnished to do so, subject to the following conditions:
|
14
|
+
*
|
15
|
+
* The above copyright notice and this permission notice shall be included in
|
16
|
+
* all copies or substantial portions of the Software.
|
17
|
+
*
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
* THE SOFTWARE.
|
25
|
+
*/
|
26
|
+
#ifndef _PASSENGER_LOGGING_KIT_FORWARD_H_
|
27
|
+
#define _PASSENGER_LOGGING_KIT_FORWARD_H_
|
28
|
+
|
29
|
+
#include <StaticString.h>
|
30
|
+
#include <jsoncpp/json.h>
|
31
|
+
|
32
|
+
namespace Passenger {
|
33
|
+
#ifndef _PASSENGER_FAST_STRING_STREAM_FORWARD_DECLARED_
|
34
|
+
#define _PASSENGER_FAST_STRING_STREAM_FORWARD_DECLARED_
|
35
|
+
template<size_t staticCapacity = 1024>
|
36
|
+
class FastStringStream;
|
37
|
+
#endif
|
38
|
+
|
39
|
+
namespace ConfigKit {
|
40
|
+
class Schema;
|
41
|
+
class Store;
|
42
|
+
class Error;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
namespace Passenger {
|
47
|
+
namespace LoggingKit {
|
48
|
+
|
49
|
+
|
50
|
+
class Schema;
|
51
|
+
struct ConfigRealization;
|
52
|
+
class Context;
|
53
|
+
|
54
|
+
enum Level {
|
55
|
+
CRIT = 0,
|
56
|
+
ERROR = 1,
|
57
|
+
WARN = 2,
|
58
|
+
NOTICE = 3,
|
59
|
+
INFO = 4,
|
60
|
+
DEBUG = 5,
|
61
|
+
DEBUG2 = 6,
|
62
|
+
DEBUG3 = 7,
|
63
|
+
|
64
|
+
UNKNOWN_LEVEL = 99
|
65
|
+
};
|
66
|
+
|
67
|
+
enum TargetType {
|
68
|
+
STDERR_TARGET,
|
69
|
+
FILE_TARGET,
|
70
|
+
NO_TARGET,
|
71
|
+
UNKNOWN_TARGET
|
72
|
+
};
|
73
|
+
|
74
|
+
extern Context *context;
|
75
|
+
|
76
|
+
|
77
|
+
void initialize(const Json::Value &initialConfig = Json::Value());
|
78
|
+
|
79
|
+
const char *_strdupFastStringStream(const FastStringStream<> &stream);
|
80
|
+
bool _passesLogLevel(const Context *context, Level level, const ConfigRealization **outputConfigRlz);
|
81
|
+
bool _shouldLogFileDescriptors(const Context *context, const ConfigRealization **outputConfigRlz);
|
82
|
+
void _prepareLogEntry(FastStringStream<> &sstream, Level level, const char *file, unsigned int line);
|
83
|
+
void _writeLogEntry(const ConfigRealization *configRlz, const char *str, unsigned int size);
|
84
|
+
void _writeFileDescriptorLogEntry(const ConfigRealization *configRlz, const char *str, unsigned int size);
|
85
|
+
|
86
|
+
Level getLevel();
|
87
|
+
void setLevel(Level level);
|
88
|
+
Level parseLevel(const StaticString &name);
|
89
|
+
StaticString levelToString(Level level);
|
90
|
+
|
91
|
+
|
92
|
+
} // namespace LoggingKit
|
93
|
+
} // namespace Passenger
|
94
|
+
|
95
|
+
#endif /* _PASSENGER_LOGGING_KIT_FORWARD_H_ */
|
@@ -0,0 +1,695 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2010-2017 Phusion Holding B.V.
|
4
|
+
*
|
5
|
+
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
|
+
* trademarks of Phusion Holding B.V.
|
7
|
+
*
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
13
|
+
* furnished to do so, subject to the following conditions:
|
14
|
+
*
|
15
|
+
* The above copyright notice and this permission notice shall be included in
|
16
|
+
* all copies or substantial portions of the Software.
|
17
|
+
*
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
* THE SOFTWARE.
|
25
|
+
*/
|
26
|
+
|
27
|
+
#include <ios>
|
28
|
+
#include <algorithm>
|
29
|
+
#include <stdexcept>
|
30
|
+
#include <cstdio>
|
31
|
+
#include <cstddef>
|
32
|
+
#include <cstring>
|
33
|
+
#include <cerrno>
|
34
|
+
#include <cassert>
|
35
|
+
#include <sys/time.h>
|
36
|
+
#include <fcntl.h>
|
37
|
+
#include <unistd.h>
|
38
|
+
#include <time.h>
|
39
|
+
#include <pthread.h>
|
40
|
+
|
41
|
+
#include <boost/cstdint.hpp>
|
42
|
+
#include <oxt/system_calls.hpp>
|
43
|
+
#include <oxt/thread.hpp>
|
44
|
+
#include <oxt/detail/context.hpp>
|
45
|
+
|
46
|
+
#include <Constants.h>
|
47
|
+
#include <StaticString.h>
|
48
|
+
#include <Exceptions.h>
|
49
|
+
#include <LoggingKit/Logging.h>
|
50
|
+
#include <LoggingKit/Assert.h>
|
51
|
+
#include <LoggingKit/Config.h>
|
52
|
+
#include <LoggingKit/Context.h>
|
53
|
+
#include <ConfigKit/ConfigKit.h>
|
54
|
+
#include <Utils.h>
|
55
|
+
#include <Utils/StrIntUtils.h>
|
56
|
+
|
57
|
+
namespace Passenger {
|
58
|
+
namespace LoggingKit {
|
59
|
+
|
60
|
+
using namespace std;
|
61
|
+
|
62
|
+
|
63
|
+
#define TRUNCATE_LOGPATHS_TO_MAXCHARS 3 // set to 0 to disable truncation
|
64
|
+
|
65
|
+
Context *context = NULL;
|
66
|
+
AssertionFailureInfo lastAssertionFailure;
|
67
|
+
|
68
|
+
|
69
|
+
void
|
70
|
+
initialize(const Json::Value &initialConfig) {
|
71
|
+
context = new Context(initialConfig);
|
72
|
+
}
|
73
|
+
|
74
|
+
|
75
|
+
Level getLevel() {
|
76
|
+
if (OXT_LIKELY(context != NULL)) {
|
77
|
+
return context->getConfigRealization()->level;
|
78
|
+
} else {
|
79
|
+
return Level(DEFAULT_LOG_LEVEL);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
void
|
84
|
+
setLevel(Level level) {
|
85
|
+
Json::Value config;
|
86
|
+
vector<ConfigKit::Error> errors;
|
87
|
+
ConfigChangeRequest req;
|
88
|
+
|
89
|
+
config["level"] = levelToString(level).toString();
|
90
|
+
if (context->prepareConfigChange(config, errors, req)) {
|
91
|
+
context->commitConfigChange(req);
|
92
|
+
} else {
|
93
|
+
P_BUG("Error setting log level: " << ConfigKit::toString(errors));
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
Level
|
98
|
+
parseLevel(const StaticString &name) {
|
99
|
+
if (name == "crit" || name == "0") {
|
100
|
+
return CRIT;
|
101
|
+
} else if (name == "error" || name == "1") {
|
102
|
+
return ERROR;
|
103
|
+
} else if (name == "warn" || name == "2") {
|
104
|
+
return WARN;
|
105
|
+
} else if (name == "notice" || name == "3") {
|
106
|
+
return NOTICE;
|
107
|
+
} else if (name == "info" || name == "4") {
|
108
|
+
return INFO;
|
109
|
+
} else if (name == "debug" || name == "5") {
|
110
|
+
return DEBUG;
|
111
|
+
} else if (name == "debug2" || name == "6") {
|
112
|
+
return DEBUG2;
|
113
|
+
} else if (name == "debug3" || name == "7") {
|
114
|
+
return DEBUG3;
|
115
|
+
} else {
|
116
|
+
return UNKNOWN_LEVEL;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
StaticString
|
121
|
+
levelToString(Level level) {
|
122
|
+
switch (level) {
|
123
|
+
case CRIT:
|
124
|
+
return P_STATIC_STRING("crit");
|
125
|
+
case ERROR:
|
126
|
+
return P_STATIC_STRING("error");
|
127
|
+
case WARN:
|
128
|
+
return P_STATIC_STRING("warn");
|
129
|
+
case NOTICE:
|
130
|
+
return P_STATIC_STRING("notice");
|
131
|
+
case INFO:
|
132
|
+
return P_STATIC_STRING("info");
|
133
|
+
case DEBUG:
|
134
|
+
return P_STATIC_STRING("debug");
|
135
|
+
case DEBUG2:
|
136
|
+
return P_STATIC_STRING("debug2");
|
137
|
+
case DEBUG3:
|
138
|
+
return P_STATIC_STRING("debug3");
|
139
|
+
default:
|
140
|
+
return P_STATIC_STRING("unknown");
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
|
145
|
+
const char *
|
146
|
+
_strdupFastStringStream(const FastStringStream<> &stream) {
|
147
|
+
char *buf = (char *) malloc(stream.size() + 1);
|
148
|
+
memcpy(buf, stream.data(), stream.size());
|
149
|
+
buf[stream.size()] = '\0';
|
150
|
+
return buf;
|
151
|
+
}
|
152
|
+
|
153
|
+
bool
|
154
|
+
_passesLogLevel(const Context *context, Level level, const ConfigRealization **outputConfigRlz) {
|
155
|
+
if (OXT_UNLIKELY(context == NULL)) {
|
156
|
+
*outputConfigRlz = NULL;
|
157
|
+
return Level(DEFAULT_LOG_LEVEL) >= level;
|
158
|
+
} else {
|
159
|
+
const ConfigRealization *configRlz = context->getConfigRealization();
|
160
|
+
*outputConfigRlz = configRlz;
|
161
|
+
return configRlz->level >= level;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
bool
|
166
|
+
_shouldLogFileDescriptors(const Context *context, const ConfigRealization **outputConfigRlz) {
|
167
|
+
if (OXT_UNLIKELY(context == NULL)) {
|
168
|
+
return false;
|
169
|
+
} else {
|
170
|
+
const ConfigRealization *configRlz = context->getConfigRealization();
|
171
|
+
*outputConfigRlz = configRlz;
|
172
|
+
return configRlz->fileDescriptorLogTargetType != NO_TARGET;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
void
|
177
|
+
_prepareLogEntry(FastStringStream<> &sstream, Level level, const char *file, unsigned int line) {
|
178
|
+
struct tm the_tm;
|
179
|
+
char datetime_buf[32];
|
180
|
+
char threadIdBuf[std::max<unsigned int>(
|
181
|
+
std::max<unsigned int>(
|
182
|
+
2 * sizeof(boost::uintptr_t) + 1,
|
183
|
+
2 * sizeof(unsigned int) + 1
|
184
|
+
),
|
185
|
+
32
|
186
|
+
)];
|
187
|
+
int datetime_size;
|
188
|
+
unsigned int threadIdSize;
|
189
|
+
struct timeval tv;
|
190
|
+
StaticString logLevelMarkers[] = {
|
191
|
+
P_STATIC_STRING("C"),
|
192
|
+
P_STATIC_STRING("E"),
|
193
|
+
P_STATIC_STRING("W"),
|
194
|
+
P_STATIC_STRING("N"),
|
195
|
+
P_STATIC_STRING("I"),
|
196
|
+
P_STATIC_STRING("D"),
|
197
|
+
P_STATIC_STRING("D2"),
|
198
|
+
P_STATIC_STRING("D3")
|
199
|
+
};
|
200
|
+
|
201
|
+
gettimeofday(&tv, NULL);
|
202
|
+
localtime_r(&tv.tv_sec, &the_tm);
|
203
|
+
datetime_size = snprintf(datetime_buf, sizeof(datetime_buf),
|
204
|
+
"%d-%02d-%02d %02d:%02d:%02d.%04llu",
|
205
|
+
the_tm.tm_year + 1900, the_tm.tm_mon + 1, the_tm.tm_mday,
|
206
|
+
the_tm.tm_hour, the_tm.tm_min, the_tm.tm_sec,
|
207
|
+
(unsigned long long) tv.tv_usec / 100);
|
208
|
+
|
209
|
+
#ifdef OXT_THREAD_LOCAL_KEYWORD_SUPPORTED
|
210
|
+
// We only use oxt::get_thread_local_context() if it is fast enough.
|
211
|
+
oxt::thread_local_context *ctx = oxt::get_thread_local_context();
|
212
|
+
if (OXT_LIKELY(ctx != NULL)) {
|
213
|
+
threadIdSize = integerToHexatri(ctx->thread_number,
|
214
|
+
threadIdBuf);
|
215
|
+
} else {
|
216
|
+
threadIdSize = integerToHexatri((boost::uintptr_t) pthread_self(),
|
217
|
+
threadIdBuf);
|
218
|
+
}
|
219
|
+
#else
|
220
|
+
threadIdSize = integerToHexatri((boost::uintptr_t) pthread_self(),
|
221
|
+
threadIdBuf);
|
222
|
+
#endif
|
223
|
+
|
224
|
+
sstream <<
|
225
|
+
P_STATIC_STRING("[ ") <<
|
226
|
+
logLevelMarkers[int(level)] <<
|
227
|
+
P_STATIC_STRING(" ") <<
|
228
|
+
StaticString(datetime_buf, datetime_size) <<
|
229
|
+
P_STATIC_STRING(" ") <<
|
230
|
+
std::dec << getpid() <<
|
231
|
+
P_STATIC_STRING("/T") <<
|
232
|
+
StaticString(threadIdBuf, threadIdSize) <<
|
233
|
+
P_STATIC_STRING(" ");
|
234
|
+
|
235
|
+
if (startsWith(file, P_STATIC_STRING("src/"))) { // special reduncancy filter because most code resides in these paths
|
236
|
+
file += sizeof("src/") - 1;
|
237
|
+
if (startsWith(file, P_STATIC_STRING("cxx_supportlib/"))) {
|
238
|
+
file += sizeof("cxx_supportlib/") - 1;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
if (TRUNCATE_LOGPATHS_TO_MAXCHARS > 0) {
|
243
|
+
truncateBeforeTokens(file, P_STATIC_STRING("/\\"), TRUNCATE_LOGPATHS_TO_MAXCHARS, sstream);
|
244
|
+
} else {
|
245
|
+
sstream << file;
|
246
|
+
}
|
247
|
+
|
248
|
+
sstream << P_STATIC_STRING(":") <<
|
249
|
+
line << P_STATIC_STRING(" ]: ");
|
250
|
+
}
|
251
|
+
|
252
|
+
static void
|
253
|
+
writeExactWithoutOXT(int fd, const char *str, unsigned int size) {
|
254
|
+
/* We do not use writeExact() here because writeExact()
|
255
|
+
* uses oxt::syscalls::write(), which is an interruption point and
|
256
|
+
* which is slightly more expensive than a plain write().
|
257
|
+
* Logging may block, but in most cases not indefinitely,
|
258
|
+
* so we don't care if the write() here is not an interruption
|
259
|
+
* point. If the write does block indefinitely then it's
|
260
|
+
* probably a FIFO that is not opened on the other side.
|
261
|
+
* In that case we can blame the user.
|
262
|
+
*/
|
263
|
+
ssize_t ret;
|
264
|
+
unsigned int written = 0;
|
265
|
+
while (written < size) {
|
266
|
+
do {
|
267
|
+
ret = write(fd, str + written, size - written);
|
268
|
+
} while (ret == -1 && errno == EINTR);
|
269
|
+
if (ret == -1) {
|
270
|
+
/* The most likely reason why this fails is when the user has setup
|
271
|
+
* Apache to log to a pipe (e.g. to a log rotation script). Upon
|
272
|
+
* restarting the web server, the process that reads from the pipe
|
273
|
+
* shuts down, so we can't write to it anymore. That's why we
|
274
|
+
* just ignore write errors. It doesn't make sense to abort for
|
275
|
+
* something like this.
|
276
|
+
*/
|
277
|
+
break;
|
278
|
+
} else {
|
279
|
+
written += ret;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
void
|
285
|
+
_writeLogEntry(const ConfigRealization *configRealization, const char *str, unsigned int size) {
|
286
|
+
if (OXT_LIKELY(configRealization != NULL)) {
|
287
|
+
writeExactWithoutOXT(configRealization->targetFd, str, size);
|
288
|
+
} else {
|
289
|
+
writeExactWithoutOXT(STDERR_FILENO, str, size);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
void
|
294
|
+
_writeFileDescriptorLogEntry(const ConfigRealization *configRealization,
|
295
|
+
const char *str, unsigned int size)
|
296
|
+
{
|
297
|
+
assert(configRealization != NULL);
|
298
|
+
assert(configRealization->fileDescriptorLogTargetType != UNKNOWN_TARGET);
|
299
|
+
assert(configRealization->fileDescriptorLogTargetFd != -1);
|
300
|
+
writeExactWithoutOXT(configRealization->fileDescriptorLogTargetFd, str, size);
|
301
|
+
}
|
302
|
+
|
303
|
+
static void
|
304
|
+
realLogAppOutput(int targetFd, char *buf, unsigned int bufSize,
|
305
|
+
const char *pidStr, unsigned int pidStrLen,
|
306
|
+
const char *channelName, unsigned int channelNameLen,
|
307
|
+
const char *message, unsigned int messageLen)
|
308
|
+
{
|
309
|
+
char *pos = buf;
|
310
|
+
char *end = buf + bufSize;
|
311
|
+
|
312
|
+
pos = appendData(pos, end, "App ");
|
313
|
+
pos = appendData(pos, end, pidStr, pidStrLen);
|
314
|
+
pos = appendData(pos, end, " ");
|
315
|
+
pos = appendData(pos, end, channelName, channelNameLen);
|
316
|
+
pos = appendData(pos, end, ": ");
|
317
|
+
pos = appendData(pos, end, message, messageLen);
|
318
|
+
pos = appendData(pos, end, "\n");
|
319
|
+
writeExactWithoutOXT(targetFd, buf, pos - buf);
|
320
|
+
}
|
321
|
+
|
322
|
+
void
|
323
|
+
logAppOutput(pid_t pid, const char *channelName, const char *message, unsigned int size) {
|
324
|
+
int targetFd;
|
325
|
+
|
326
|
+
if (OXT_LIKELY(context != NULL)) {
|
327
|
+
const ConfigRealization *configRealization = context->getConfigRealization();
|
328
|
+
if (configRealization->level < configRealization->appOutputLogLevel) {
|
329
|
+
return;
|
330
|
+
}
|
331
|
+
|
332
|
+
targetFd = configRealization->targetFd;
|
333
|
+
} else {
|
334
|
+
targetFd = STDERR_FILENO;
|
335
|
+
}
|
336
|
+
|
337
|
+
char pidStr[sizeof("4294967295")];
|
338
|
+
unsigned int pidStrLen, channelNameLen, totalLen;
|
339
|
+
|
340
|
+
try {
|
341
|
+
pidStrLen = integerToOtherBase<pid_t, 10>(pid, pidStr, sizeof(pidStr));
|
342
|
+
} catch (const std::length_error &) {
|
343
|
+
pidStr[0] = '?';
|
344
|
+
pidStr[1] = '\0';
|
345
|
+
pidStrLen = 1;
|
346
|
+
}
|
347
|
+
|
348
|
+
channelNameLen = strlen(channelName);
|
349
|
+
totalLen = (sizeof("App X Y: \n") - 2) + pidStrLen + channelNameLen + size;
|
350
|
+
if (totalLen < 1024) {
|
351
|
+
char buf[1024];
|
352
|
+
realLogAppOutput(targetFd,
|
353
|
+
buf, sizeof(buf),
|
354
|
+
pidStr, pidStrLen,
|
355
|
+
channelName, channelNameLen,
|
356
|
+
message, size);
|
357
|
+
} else {
|
358
|
+
DynamicBuffer buf(totalLen);
|
359
|
+
realLogAppOutput(targetFd,
|
360
|
+
buf.data, totalLen,
|
361
|
+
pidStr, pidStrLen,
|
362
|
+
channelName, channelNameLen,
|
363
|
+
message, size);
|
364
|
+
}
|
365
|
+
}
|
366
|
+
|
367
|
+
|
368
|
+
static Json::Value
|
369
|
+
normalizeConfig(const Json::Value &effectiveValues) {
|
370
|
+
Json::Value updates(Json::objectValue);
|
371
|
+
|
372
|
+
updates["level"] = levelToString(parseLevel(
|
373
|
+
effectiveValues["level"].asString())).toString();
|
374
|
+
updates["app_output_log_level"] = levelToString(parseLevel(
|
375
|
+
effectiveValues["app_output_log_level"].asString())).toString();
|
376
|
+
|
377
|
+
if (effectiveValues["target"].isString()) {
|
378
|
+
updates["target"]["path"] = absolutizePath(effectiveValues["target"].asString());
|
379
|
+
} else if (!effectiveValues["target"]["path"].isNull()) {
|
380
|
+
updates["target"] = effectiveValues["target"];
|
381
|
+
updates["target"]["path"] = absolutizePath(effectiveValues["target"]["path"].asString());
|
382
|
+
}
|
383
|
+
|
384
|
+
if (effectiveValues["file_descriptor_log_target"].isString()) {
|
385
|
+
updates["file_descriptor_log_target"]["path"] =
|
386
|
+
absolutizePath(effectiveValues["file_descriptor_log_target"].asString());
|
387
|
+
} else if (effectiveValues["file_descriptor_log_target"].isObject()
|
388
|
+
&& !effectiveValues["file_descriptor_log_target"]["path"].isNull())
|
389
|
+
{
|
390
|
+
updates["file_descriptor_log_target"] = effectiveValues["file_descriptor_log_target"];
|
391
|
+
updates["file_descriptor_log_target"]["path"] =
|
392
|
+
absolutizePath(effectiveValues["file_descriptor_log_target"]["path"].asString());
|
393
|
+
}
|
394
|
+
|
395
|
+
return updates;
|
396
|
+
}
|
397
|
+
|
398
|
+
|
399
|
+
Context::Context(const Json::Value &initialConfig)
|
400
|
+
: config(schema, initialConfig)
|
401
|
+
{
|
402
|
+
configRlz.store(new ConfigRealization(config));
|
403
|
+
configRlz.load()->apply(config, NULL);
|
404
|
+
configRlz.load()->finalize();
|
405
|
+
}
|
406
|
+
|
407
|
+
ConfigKit::Store
|
408
|
+
Context::getConfig() const {
|
409
|
+
boost::lock_guard<boost::mutex> l(syncher);
|
410
|
+
return config;
|
411
|
+
}
|
412
|
+
|
413
|
+
bool
|
414
|
+
Context::prepareConfigChange(const Json::Value &updates,
|
415
|
+
vector<ConfigKit::Error> &errors, LoggingKit::ConfigChangeRequest &req)
|
416
|
+
{
|
417
|
+
{
|
418
|
+
boost::lock_guard<boost::mutex> l(syncher);
|
419
|
+
req.config.reset(new ConfigKit::Store(config, updates, errors));
|
420
|
+
}
|
421
|
+
if (!errors.empty()) {
|
422
|
+
return false;
|
423
|
+
}
|
424
|
+
|
425
|
+
req.configRlz = new ConfigRealization(*req.config);
|
426
|
+
return true;
|
427
|
+
}
|
428
|
+
|
429
|
+
void
|
430
|
+
Context::commitConfigChange(LoggingKit::ConfigChangeRequest &req) BOOST_NOEXCEPT_OR_NOTHROW {
|
431
|
+
boost::lock_guard<boost::mutex> l(syncher);
|
432
|
+
ConfigRealization *oldConfigRlz = configRlz.load();
|
433
|
+
ConfigRealization *newConfigRlz = req.configRlz;
|
434
|
+
|
435
|
+
req.configRlz->apply(*req.config, oldConfigRlz);
|
436
|
+
|
437
|
+
config.swap(*req.config);
|
438
|
+
|
439
|
+
configRlz.store(newConfigRlz, boost::memory_order_release);
|
440
|
+
req.configRlz = NULL; // oldConfigRlz will be garbage collected by apply()
|
441
|
+
|
442
|
+
newConfigRlz->finalize();
|
443
|
+
}
|
444
|
+
|
445
|
+
|
446
|
+
Json::Value
|
447
|
+
Schema::createStderrTarget() {
|
448
|
+
Json::Value doc;
|
449
|
+
doc["stderr"] = true;
|
450
|
+
return doc;
|
451
|
+
}
|
452
|
+
|
453
|
+
void
|
454
|
+
Schema::validateLogLevel(const string &key, const ConfigKit::Store &store,
|
455
|
+
vector<ConfigKit::Error> &errors)
|
456
|
+
{
|
457
|
+
typedef ConfigKit::Error Error;
|
458
|
+
Level level = parseLevel(store[key].asString());
|
459
|
+
if (level == UNKNOWN_LEVEL) {
|
460
|
+
errors.push_back(Error("'{{" + key + "}}' must be one of"
|
461
|
+
" 'crit', 'error', 'warn', 'notice', 'info', 'debug', 'debug2' or 'debug3'"));
|
462
|
+
}
|
463
|
+
}
|
464
|
+
|
465
|
+
void
|
466
|
+
Schema::validateTarget(const string &key, const ConfigKit::Store &store,
|
467
|
+
vector<ConfigKit::Error> &errors)
|
468
|
+
{
|
469
|
+
typedef ConfigKit::Error Error;
|
470
|
+
Json::Value value = store[key];
|
471
|
+
string keyQuote = "'{{" + key + "}}'";
|
472
|
+
|
473
|
+
if (value.isNull()) {
|
474
|
+
return;
|
475
|
+
}
|
476
|
+
|
477
|
+
if (value.isObject()) {
|
478
|
+
if (value.isMember("stderr")) {
|
479
|
+
if (value.size() > 1) {
|
480
|
+
errors.push_back(Error("When " + keyQuote
|
481
|
+
+ " is an object containing the 'stderr' key,"
|
482
|
+
" it may not contain any other keys"));
|
483
|
+
} else if (!value["stderr"].asBool()) {
|
484
|
+
errors.push_back(Error("When " + keyQuote
|
485
|
+
+ " is an object containing the 'stderr' key,"
|
486
|
+
" it must have the 'true' value"));
|
487
|
+
}
|
488
|
+
} else if (value.isMember("path")) {
|
489
|
+
if (!value["path"].isString()) {
|
490
|
+
errors.push_back(Error("When " + keyQuote
|
491
|
+
+ " is an object containing the 'path' key,"
|
492
|
+
" it must be a string"));
|
493
|
+
}
|
494
|
+
if (value.isMember("fd")) {
|
495
|
+
if (!value["fd"].isInt()) {
|
496
|
+
errors.push_back(Error("When " + keyQuote
|
497
|
+
+ " is an object containing the 'fd' key,"
|
498
|
+
" it must be an integer"));
|
499
|
+
} else if (value["fd"].asInt() < 0) {
|
500
|
+
errors.push_back(Error("When " + keyQuote
|
501
|
+
+ " is an object containing the 'fd' key,"
|
502
|
+
" it must be 0 or greater"));
|
503
|
+
}
|
504
|
+
}
|
505
|
+
} else {
|
506
|
+
errors.push_back(Error("When " + keyQuote
|
507
|
+
+ " is an object, it must contain either"
|
508
|
+
" the 'stderr' or 'path' key"));
|
509
|
+
}
|
510
|
+
} else if (!value.isString()) {
|
511
|
+
errors.push_back(Error(keyQuote
|
512
|
+
+ " must be either a string or an object"));
|
513
|
+
}
|
514
|
+
}
|
515
|
+
|
516
|
+
static Json::Value
|
517
|
+
filterTargetFd(const Json::Value &value) {
|
518
|
+
Json::Value result = value;
|
519
|
+
result.removeMember("fd");
|
520
|
+
return result;
|
521
|
+
}
|
522
|
+
|
523
|
+
Schema::Schema() {
|
524
|
+
using namespace ConfigKit;
|
525
|
+
|
526
|
+
add("level", STRING_TYPE, OPTIONAL, DEFAULT_LOG_LEVEL_NAME);
|
527
|
+
add("target", ANY_TYPE, OPTIONAL, createStderrTarget())
|
528
|
+
.setInspectFilter(filterTargetFd);
|
529
|
+
add("file_descriptor_log_target", ANY_TYPE, OPTIONAL)
|
530
|
+
.setInspectFilter(filterTargetFd);
|
531
|
+
add("redirect_stderr", BOOL_TYPE, OPTIONAL, true);
|
532
|
+
add("app_output_log_level", STRING_TYPE, OPTIONAL, DEFAULT_APP_OUTPUT_LOG_LEVEL_NAME);
|
533
|
+
|
534
|
+
addValidator(boost::bind(validateLogLevel, "level",
|
535
|
+
boost::placeholders::_1, boost::placeholders::_2));
|
536
|
+
addValidator(boost::bind(validateLogLevel, "app_output_log_level",
|
537
|
+
boost::placeholders::_1, boost::placeholders::_2));
|
538
|
+
addValidator(boost::bind(validateTarget, "target",
|
539
|
+
boost::placeholders::_1, boost::placeholders::_2));
|
540
|
+
addValidator(boost::bind(validateTarget, "file_descriptor_log_target",
|
541
|
+
boost::placeholders::_1, boost::placeholders::_2));
|
542
|
+
|
543
|
+
addNormalizer(normalizeConfig);
|
544
|
+
|
545
|
+
finalize();
|
546
|
+
}
|
547
|
+
|
548
|
+
|
549
|
+
ConfigRealization::ConfigRealization(const ConfigKit::Store &store)
|
550
|
+
: level(parseLevel(store["level"].asString())),
|
551
|
+
appOutputLogLevel(parseLevel(store["app_output_log_level"].asString())),
|
552
|
+
finalized(false)
|
553
|
+
{
|
554
|
+
if (store["target"].isMember("stderr")) {
|
555
|
+
targetType = STDERR_TARGET;
|
556
|
+
targetFd = STDERR_FILENO;
|
557
|
+
targetFdClosePolicy = NEVER_CLOSE;
|
558
|
+
} else if (store["target"]["fd"].isNull()) {
|
559
|
+
string path = store["target"]["path"].asString();
|
560
|
+
targetType = FILE_TARGET;
|
561
|
+
targetFd = syscalls::open(path.c_str(),
|
562
|
+
O_WRONLY | O_APPEND | O_CREAT, 0644);
|
563
|
+
if (targetFd == -1) {
|
564
|
+
int e = errno;
|
565
|
+
throw FileSystemException(
|
566
|
+
"Cannot open " + path + " for writing",
|
567
|
+
e, path);
|
568
|
+
}
|
569
|
+
targetFdClosePolicy = ALWAYS_CLOSE;
|
570
|
+
} else {
|
571
|
+
targetType = FILE_TARGET;
|
572
|
+
targetFd = store["target"]["fd"].asInt();
|
573
|
+
// If anything goes wrong before finalization, then
|
574
|
+
// the caller is responsible for cleaning up the fd.
|
575
|
+
// See the Context class description.
|
576
|
+
targetFdClosePolicy = CLOSE_WHEN_FINALIZED;
|
577
|
+
}
|
578
|
+
|
579
|
+
if (store["file_descriptor_log_target"].isNull()) {
|
580
|
+
fileDescriptorLogTargetType = NO_TARGET;
|
581
|
+
fileDescriptorLogTargetFd = -1;
|
582
|
+
fileDescriptorLogTargetFdClosePolicy = NEVER_CLOSE;
|
583
|
+
} else if (store["file_descriptor_log_target"].isMember("stderr")) {
|
584
|
+
fileDescriptorLogTargetType = STDERR_TARGET;
|
585
|
+
fileDescriptorLogTargetFd = STDERR_FILENO;
|
586
|
+
fileDescriptorLogTargetFdClosePolicy = NEVER_CLOSE;
|
587
|
+
} else if (store["file_descriptor_log_target"]["fd"].isNull()) {
|
588
|
+
string path = store["file_descriptor_log_target"]["path"].asString();
|
589
|
+
fileDescriptorLogTargetType = FILE_TARGET;
|
590
|
+
fileDescriptorLogTargetFd = syscalls::open(path.c_str(),
|
591
|
+
O_WRONLY | O_APPEND | O_CREAT, 0644);
|
592
|
+
if (fileDescriptorLogTargetFd == -1) {
|
593
|
+
int e = errno;
|
594
|
+
throw FileSystemException(
|
595
|
+
"Cannot open " + path + " for writing",
|
596
|
+
e, path);
|
597
|
+
}
|
598
|
+
fileDescriptorLogTargetFdClosePolicy = ALWAYS_CLOSE;
|
599
|
+
} else {
|
600
|
+
fileDescriptorLogTargetType = FILE_TARGET;
|
601
|
+
fileDescriptorLogTargetFd = store["file_descriptor_log_target"]["fd"].asInt();
|
602
|
+
// If anything goes wrong before finalization, then
|
603
|
+
// the caller is responsible for cleaning up the fd.
|
604
|
+
// See the Context class description.
|
605
|
+
fileDescriptorLogTargetFdClosePolicy = CLOSE_WHEN_FINALIZED;
|
606
|
+
}
|
607
|
+
}
|
608
|
+
|
609
|
+
ConfigRealization::~ConfigRealization() {
|
610
|
+
switch (targetFdClosePolicy) {
|
611
|
+
case NEVER_CLOSE:
|
612
|
+
// Do nothing.
|
613
|
+
break;
|
614
|
+
case ALWAYS_CLOSE:
|
615
|
+
syscalls::close(targetFd);
|
616
|
+
break;
|
617
|
+
case CLOSE_WHEN_FINALIZED:
|
618
|
+
if (finalized) {
|
619
|
+
syscalls::close(targetFd);
|
620
|
+
}
|
621
|
+
break;
|
622
|
+
}
|
623
|
+
|
624
|
+
switch (fileDescriptorLogTargetFdClosePolicy) {
|
625
|
+
case NEVER_CLOSE:
|
626
|
+
// Do nothing.
|
627
|
+
break;
|
628
|
+
case ALWAYS_CLOSE:
|
629
|
+
syscalls::close(fileDescriptorLogTargetFd);
|
630
|
+
break;
|
631
|
+
case CLOSE_WHEN_FINALIZED:
|
632
|
+
if (finalized) {
|
633
|
+
syscalls::close(fileDescriptorLogTargetFd);
|
634
|
+
}
|
635
|
+
break;
|
636
|
+
}
|
637
|
+
}
|
638
|
+
|
639
|
+
static void
|
640
|
+
garbageCollectConfigRealization(ConfigRealization *configRlz) {
|
641
|
+
boost::this_thread::disable_interruption di;
|
642
|
+
boost::this_thread::disable_syscall_interruption dsi;
|
643
|
+
syscalls::sleep(30);
|
644
|
+
delete configRlz;
|
645
|
+
}
|
646
|
+
|
647
|
+
void
|
648
|
+
ConfigRealization::apply(const ConfigKit::Store &config, ConfigRealization *oldConfigRlz)
|
649
|
+
BOOST_NOEXCEPT_OR_NOTHROW
|
650
|
+
{
|
651
|
+
if (config["redirect_stderr"].asBool()) {
|
652
|
+
int ret = syscalls::dup2(targetFd, STDERR_FILENO);
|
653
|
+
if (ret == -1) {
|
654
|
+
int e = errno;
|
655
|
+
P_ERROR("Error redirecting logging target to stderr: "
|
656
|
+
<< strerror(e) << " (errno=" << e << ")");
|
657
|
+
}
|
658
|
+
}
|
659
|
+
|
660
|
+
if (oldConfigRlz != NULL) {
|
661
|
+
// Garbage collect old config realization in 30 seconds.
|
662
|
+
// There is no way to cheaply find out whether oldConfigRlz
|
663
|
+
// is still being used (we don't want to resort to more atomic
|
664
|
+
// operations, or conservative garbage collection) but
|
665
|
+
// waiting 30 seconds should be good enough.
|
666
|
+
try {
|
667
|
+
oxt::thread(boost::bind(garbageCollectConfigRealization, oldConfigRlz),
|
668
|
+
"LoggingKit config garbage collector " + toString(oldConfigRlz),
|
669
|
+
128 * 1024);
|
670
|
+
} catch (const std::exception &e) {
|
671
|
+
P_ERROR("Error spawning background thread to garbage collect"
|
672
|
+
" old LoggingKit configuration: " << e.what());
|
673
|
+
}
|
674
|
+
}
|
675
|
+
}
|
676
|
+
|
677
|
+
void
|
678
|
+
ConfigRealization::finalize() {
|
679
|
+
finalized = true;
|
680
|
+
}
|
681
|
+
|
682
|
+
|
683
|
+
ConfigChangeRequest::ConfigChangeRequest()
|
684
|
+
: configRlz(NULL)
|
685
|
+
{
|
686
|
+
// Do nothing.
|
687
|
+
}
|
688
|
+
|
689
|
+
ConfigChangeRequest::~ConfigChangeRequest() {
|
690
|
+
delete configRlz;
|
691
|
+
}
|
692
|
+
|
693
|
+
|
694
|
+
} // namespace LoggingKit
|
695
|
+
} // namespace Passenger
|