Adding logging framework and config to pubd.

This commit is contained in:
Tim Bruijnzeels
2018-12-19 15:22:02 +01:00
parent b95268c460
commit 301a8f0302
6 changed files with 237 additions and 125 deletions
+4 -1
View File
@@ -11,10 +11,12 @@ bcder = "^0.1"
bytes = "^0.4"
chrono = { version = "^0.4", features = ["serde"] }
clap = "^2.32"
futures = "0.1"
failure = "^0.1"
fern = "^0.5"
futures = "0.1"
hex = "^0.3"
lazy_static = "^1.1"
log = "^0.4"
openssl = "^0.10"
pretty = "0.5.2"
rand = "^0.5"
@@ -23,6 +25,7 @@ rpki = { version = "^0.2", features = ["softkeys"] }
serde = { version = "^1.0", features = ["rc"] }
serde_derive = "^1.0"
serde_json = "^1.0"
syslog = "^4.0"
toml = "^0.4"
tokio = "^0.1"
xml-rs = "0.8.0"
@@ -26,4 +26,31 @@ rsync_base = "rsync://127.0.0.1/repo/"
rrdp_base_uri = "http://127.0.0.1:3000/rrdp/"
# Specify the base service URI where publishers can connect.
service_uri = "http://127.0.0.1:3000/rfc8181/"
service_uri = "http://127.0.0.1:3000/rfc8181/"
# Log level
#
# The maximum log level ("off", "error", "warn", "info", or "debug") for
# which to log messages.
#
# Defaults to "warn"
log_level = "debug"
# Log type
#
# Where to log to. One of "stderr" for stderr, "syslog" for syslog, or "file"
# for a file. If "file" is given, the "log_file" field needs to be given, too.
#
# Defaults to "syslog".
log_type = "stderr"
# Syslog facility
#
# The syslog facility to log to if syslog logging is used. Defaults to "daemon".
syslog_facility = "daemon"
# Log file
#
# The path to the file to log to if file logging is used. If the path is
# relative, it is relative to the directory this config file lives in.
log_file = "myfile.log"
+39
View File
@@ -7,8 +7,12 @@ use rpki::remote::idcert::IdCert;
use rpki::signing::signer::KeyId;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de;
use log::LevelFilter;
use syslog::Facility;
//------------ Bytes ---------------------------------------------------------
pub fn de_bytes<'de, D>(d: D) -> Result<Bytes, D::Error>
where D: Deserializer<'de>
{
@@ -23,6 +27,9 @@ where S: Serializer
base64::encode(b).serialize(s)
}
//------------ uri::Rsync ----------------------------------------------------
pub fn de_rsync_uri<'de, D>(d: D) -> Result<uri::Rsync, D::Error>
where D: Deserializer<'de>
{
@@ -36,6 +43,9 @@ where S: Serializer
uri.to_string().serialize(s)
}
//------------ uri::Http -----------------------------------------------------
pub fn de_http_uri<'de, D>(d: D) -> Result<uri::Http, D::Error>
where D: Deserializer<'de>
{
@@ -49,6 +59,9 @@ where S: Serializer
uri.to_string().serialize(s)
}
//------------ IdCert --------------------------------------------------------
pub fn de_id_cert<'de, D>(d: D) -> Result<IdCert, D::Error>
where D: Deserializer<'de>
{
@@ -66,6 +79,8 @@ where S: Serializer
str.serialize(s)
}
//------------ KeyId ---------------------------------------------------------
pub fn de_key_id<'de, D>(d: D) -> Result<KeyId, D::Error>
where D: Deserializer<'de>
{
@@ -77,4 +92,28 @@ pub fn ser_key_id<S>(key_id: &KeyId, s: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
key_id.as_str().serialize(s)
}
//------------ LevelFilter ---------------------------------------------------
pub fn de_level_filter<'de, D>(d: D) -> Result<LevelFilter, D::Error>
where D: Deserializer<'de>
{
use std::str::FromStr;
let string = String::deserialize(d)?;
LevelFilter::from_str(&string).map_err(de::Error::custom)
}
//------------ Facility ------------------------------------------------------
pub fn de_facility<'de, D>(d: D) -> Result<Facility, D::Error>
where D: Deserializer<'de>
{
use std::str::FromStr;
let string = String::deserialize(d)?;
Facility::from_str(&string).map_err(
|_| { de::Error::custom(
format!("Unsupported syslog_facility: \"{}\"", string))})
}
+3 -1
View File
@@ -10,15 +10,17 @@ extern crate core;
extern crate futures;
extern crate hex;
extern crate openssl;
#[macro_use] extern crate log;
extern crate rand;
extern crate reqwest;
extern crate rpki;
#[macro_use] extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate syslog;
extern crate tokio;
extern crate toml;
extern crate xml;
extern crate reqwest;
pub mod file;
pub mod provisioning;
+3 -7
View File
@@ -1,15 +1,11 @@
use std::io;
use std::path::PathBuf;
use clap::{App, Arg};
use clap::{App, Arg, SubCommand};
use toml;
use clap::SubCommand;
/// Global configuration for the RRDP Server.
///
/// This will parse a default config file ('./defaults/server.toml') unless
/// another file is explicitly specified. Command line arguments may be used
/// to override any of the settings in the config file.
//------------ Config --------------------------------------------------------
#[derive(Debug, Deserialize)]
pub struct Config {
name: String,
+160 -115
View File
@@ -1,19 +1,39 @@
use std::fs;
use std::fs::File;
use std::io;
use std::io::Read;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;
use clap::{App, Arg};
use ext_serde;
use toml;
use log::LevelFilter;
use rpki::uri;
use syslog::Facility;
use serde::de;
use serde::{Deserialize, Deserializer};
use toml;
const SERVER_NAME: &'static str = "Publication Server";
pub struct ConfigDefaults;
impl ConfigDefaults {
fn log_level() -> LevelFilter {
LevelFilter::Warn
}
fn log_type() -> LogType {
LogType::Syslog
}
fn syslog_facility() -> Facility {
Facility::LOG_DAEMON
}
}
/// Global configuration for the RRDP Server.
///
/// This will parse a default config file ('./defaults/server.toml') unless
/// This will parse a default config file ('./defaults/pubserver.conf') unless
/// another file is explicitly specified. Command line arguments may be used
/// to override any of the settings in the config file.
#[derive(Debug, Deserialize)]
@@ -31,6 +51,23 @@ pub struct Config {
#[serde(deserialize_with = "ext_serde::de_http_uri")]
service_uri: uri::Http,
#[serde(
default = "ConfigDefaults::log_level",
deserialize_with = "ext_serde::de_level_filter"
)]
log_level: LevelFilter,
#[serde(default = "ConfigDefaults::log_type")]
log_type: LogType,
#[serde(
default = "ConfigDefaults::syslog_facility",
deserialize_with = "ext_serde::de_facility"
)]
syslog_facility: Facility,
log_file: Option<PathBuf>
}
/// # Accessors
@@ -39,23 +76,15 @@ impl Config {
SocketAddr::new(self.ip, self.port)
}
pub fn data_dir(&self) -> &PathBuf {
&self.data_dir
}
pub fn data_dir(&self) -> &PathBuf { &self.data_dir }
pub fn pub_xml_dir(&self) -> &PathBuf {
&self.pub_xml_dir
}
pub fn pub_xml_dir(&self) -> &PathBuf { &self.pub_xml_dir }
pub fn rsync_base(&self) -> &uri::Rsync { &self.rsync_base }
pub fn service_uri(&self) -> &uri::Http {
&self.service_uri
}
pub fn service_uri(&self) -> &uri::Http { &self.service_uri }
pub fn rrdp_base_uri(&self) -> &uri::Http {
&self.rrdp_base_uri
}
pub fn rrdp_base_uri(&self) -> &uri::Http { &self.rrdp_base_uri }
}
/// # Create
@@ -78,6 +107,10 @@ impl Config {
"http://127.0.0.1:3000/rrdp/").unwrap();
let service_uri = uri::Http::from_str(
"http://127.0.0.1:3000/rfc8181/").unwrap();
let log_level = ConfigDefaults::log_level();
let log_type = ConfigDefaults::log_type();
let log_file = None;
let syslog_facility = ConfigDefaults::syslog_facility();
Config {
ip,
@@ -86,7 +119,11 @@ impl Config {
pub_xml_dir,
rsync_base,
rrdp_base_uri,
service_uri
service_uri,
log_level,
log_type,
log_file,
syslog_facility
}
}
@@ -99,108 +136,18 @@ impl Config {
.long("config")
.value_name("FILE")
.help("Specify non-default config file. If no file is \
specified './defaults/server.toml' will be used to \
specified './defaults/pubserver.conf' will be used to \
determine default values for all settings. Note that you \
can use any of the following options to override any of \
these values..")
.required(false))
.arg(Arg::with_name("ip")
.short("i")
.long("ip")
.value_name("IP Address")
.help("Override the IP address.")
.required(false))
.arg(Arg::with_name("port")
.short("p")
.long("port")
.value_name("Port number")
.help("Override the port number.")
.required(false))
.arg(Arg::with_name("pub_xml_dir")
.short("x")
.long("pub_xml_dir")
.value_name("DIR")
.help("Override the directory with publisher XML files.")
.required(false))
.arg(Arg::with_name("rsync_base")
.short("r")
.long("rsync_base")
.value_name("URI")
.help("Override rsync base URI.")
.required(false))
.arg(Arg::with_name("rrdp_base_uri")
.short("n")
.long("rrdp_base_uri")
.value_name("URI")
.help("Override the RRDP base URI.")
.required(false))
.arg(Arg::with_name("service_uri")
.short("u")
.long("service_uri")
.value_name("URI")
.help("Override the service URI.")
.required(false))
.get_matches();
let config_file = matches.value_of("config")
.unwrap_or("./defaults/server.toml");
let mut c = Self::read_config(config_file.as_ref())?;
if ! fs::metadata(&c.data_dir)?.is_dir() {
return Err(
ConfigError::Other(
format!(
"Invalid data_dir: {}",
c.data_dir.to_string_lossy().as_ref()
)
)
)
}
if let Some(ip_arg) = matches.value_of("ip") {
match IpAddr::from_str(ip_arg) {
Ok(ip) => c.ip = ip,
Err(_) => return Err(
ConfigError::Other(
format!("Invalid IP Address: {}", ip_arg)
))
}
}
if let Some(port_arg) = matches.value_of("port") {
match u16::from_str(port_arg) {
Ok(p) => {
if p < 1024 {
return Err(
ConfigError::Other(
"Port number must be between 1024 and \
65535".to_string()
)
)
}
c.port = p;
}
Err(_) => return Err(
ConfigError::Other(
format!("Invalid port: {}", port_arg)))
}
}
if let Some(xml_arg) = matches.value_of("pub_xml_dir") {
c.pub_xml_dir = PathBuf::from(xml_arg)
}
if let Some(rsync_base) = matches.value_of("rsync_base") {
c.rsync_base = uri::Rsync::from_str(rsync_base)?;
}
if let Some(rrdp_base_uri) = matches.value_of("rrdp_base_uri") {
c.rrdp_base_uri = uri::Http::from_str(rrdp_base_uri)?;
}
.unwrap_or("./defaults/pubserver.conf");
let c = Self::read_config(config_file.as_ref())?;
c.init_logging()?;
Ok(c)
}
@@ -212,11 +159,61 @@ impl Config {
let c: Config = toml::from_slice(v.as_slice())?;
if c.port < 1024 {
Err(ConfigError::Other("Port number must be >1024".to_string()))
} else {
Ok(c)
return Err(ConfigError::from_str("Port number must be >1024"))
}
if c.log_type == LogType::File && c.log_file == None {
return Err(ConfigError::from_str(
"Must specify log_file if log_type is 'file'."
))
}
Ok(c)
}
pub fn init_logging(&self) -> Result<(), ConfigError> {
match self.log_type {
LogType::File => {
let file = fern::log_file(self.log_file.as_ref().unwrap())?;
let dispatch = fern::Dispatch::new()
.level(self.log_level)
.chain(file);
dispatch.apply().map_err(|e| {
ConfigError::Other(
format!("Failed to init file logging: {}", e)
)
})?;
},
LogType::Syslog => {
syslog::init(
self.syslog_facility,
self.log_level,
Some(SERVER_NAME)
).map_err(|e| {
ConfigError::Other(
format!("Failed to init syslog: {}", e)
)
})?;
},
LogType::Stderr => {
let dispatch = fern::Dispatch::new()
.level(self.log_level)
.chain(io::stderr());
dispatch.apply().map_err(|e| {
ConfigError::Other(
format!("Failed to init stderr logging: {}", e)
)
})?;
}
}
Ok(())
}
}
@@ -236,6 +233,12 @@ pub enum ConfigError {
Other(String)
}
impl ConfigError {
pub fn from_str(s: &str) -> ConfigError {
ConfigError::Other(s.to_string())
}
}
impl From<io::Error> for ConfigError {
fn from(e: io::Error) -> Self {
ConfigError::IoError(e)
@@ -255,6 +258,48 @@ impl From<uri::Error> for ConfigError {
}
//------------ LogType -------------------------------------------------------
/// The target to log to.
#[derive(Clone, Debug)]
pub enum LogType {
Syslog,
Stderr,
File
}
//--- PartialEq and Eq
impl PartialEq for LogType {
fn eq(&self, other: &LogType) -> bool {
match (self, other) {
(&LogType::Syslog, &LogType::Syslog) => true,
(&LogType::Stderr, &LogType::Stderr) => true,
(&LogType::File, &LogType::File) => true,
_ => false
}
}
}
impl Eq for LogType { }
impl<'de> Deserialize<'de> for LogType {
fn deserialize<D>(d: D) -> Result<LogType, D::Error>
where D: Deserializer<'de> {
let string = String::deserialize(d)?;
match string.as_str() {
"stderr" => Ok(LogType::Stderr),
"syslog" => Ok(LogType::Syslog),
"file" => Ok(LogType::Stderr),
_ => Err(
de::Error::custom(
format!("Unsupported log type: {}", string)))
}
}
}
//------------ Tests ---------------------------------------------------------
#[cfg(test)]
@@ -264,7 +309,7 @@ mod tests {
#[test]
fn should_parse_default_config_file() {
let c = Config::read_config("./defaults/server.toml").unwrap();
let c = Config::read_config("./defaults/pubserver.conf").unwrap();
let expected_socket_addr = ([127, 0, 0, 1], 3000).into();
assert_eq!(c.socket_addr(), expected_socket_addr);
}