#[thread_local] static mut PerThreadLocalSyslogSocket: StaticInitializedOnce<LocalSyslogSocket> = StaticInitializedOnce::uninitialized();
#[derive(Debug)]
pub struct LocalSyslogSocket
{
socket_file_descriptor: DatagramClientSocketFileDescriptor<sockaddr_un>,
is_connected: Cell<bool>,
buffer: Vec<u8>,
socket_file_path: PathBuf,
log_messages_have_been_truncated: bool,
}
impl LocalSyslogSocket
{
#[inline(always)]
pub(crate) unsafe fn configure_per_thread_local_syslog_socket() -> Result<(), NewSocketClientError>
{
PerThreadLocalSyslogSocket.initialize_once(LocalSyslogSocket::new(StaticLoggingConfiguration::instance())?);
Ok(())
}
#[inline(always)]
pub fn syslog_falling_back_to_standard_error(message_template: &impl MessageTemplate, message: &str)
{
if let Err(error_message) = Self::syslog(message_template, message)
{
write_to_standard_error_ignoring_failure(format_args!("Could not syslog: `{}` message `{}`", error_message, message))
}
}
#[inline(always)]
fn syslog(message_template: &impl MessageTemplate, message: &str) -> Result<(), &'static str>
{
Self::instance_mut().log(message_template, message)
}
#[inline(always)]
fn instance_mut() -> &'static mut Self
{
unsafe { PerThreadLocalSyslogSocket.value_mut() }
}
fn new(configuration: &StaticLoggingConfiguration) -> Result<Self, NewSocketClientError>
{
let socket_file_path = configuration.dev_path.file_path("log");
Ok
(
Self
{
socket_file_descriptor: Self::open(&socket_file_path)?,
is_connected: Cell::new(true),
buffer:
{
let mut buffer = Vec::with_capacity(configuration.logging_buffer_size.get());
unsafe { buffer.set_len(buffer.capacity()) };
buffer
},
socket_file_path,
log_messages_have_been_truncated: false,
}
)
}
fn log(&mut self, message_template: &impl MessageTemplate, message: &str) -> Result<(), &'static str>
{
if !self.is_connected.get()
{
if self.reconnect().is_err()
{
return Err("NotConnected and could not reconnect")
}
}
let buffer_length = self.write_message_to_buffer(message_template, message);
let mut buffer = &self.buffer[ ..buffer_length];
loop
{
use crate::ErrorKind::*;
match self.socket_file_descriptor.send(buffer, SendFlags::empty())
{
Ok(length_sent) => if likely!(buffer_length == length_sent)
{
return Ok(())
}
else
{
debug_assert!(length_sent < buffer_length);
buffer = &buffer[length_sent .. ];
continue
},
Err(error) => match error.kind()
{
Interrupted => continue,
Other =>
{
self.sleep_for_one_second();
continue
},
PermissionDenied =>
{
if self.reconnect().is_err()
{
return Err("PermissionDenied and could not reconnect")
}
if buffer.len() != buffer_length
{
return Err("PermissionDenied, reconnected but partial log message send has occurred")
}
continue
},
WouldBlock => unreachable_code(format_args!("socket has been opened blocking")),
_ =>
{
if self.reconnect().is_err()
{
return Err("Errored and could not reconnect")
}
if buffer.len() != buffer_length
{
return Err("Errored, reconnected but partial log message send has occurred")
}
continue
}
}
}
}
}
#[inline(always)]
fn write_message_to_buffer(&mut self, message_template: &impl MessageTemplate, message: &str) -> usize
{
let timestamp = Utc::now();
let written_length =
{
let reserve_space_for_terminal_line_feed = self.buffer.len() - 1;
let (written_length, truncated) = message_template.format(&mut self.buffer[..reserve_space_for_terminal_line_feed], timestamp, message);
if unlikely!(truncated)
{
self.log_messages_have_been_truncated = truncated;
}
written_length
};
self.write_terminal_line_feed_to_buffer(written_length)
}
#[inline(always)]
fn write_terminal_line_feed_to_buffer(&mut self, written_length: usize) -> usize
{
const LineFeed: u8 = b'\n';
self.buffer.set_unchecked_mut_safe(written_length, LineFeed);
written_length + 1
}
#[inline(always)]
fn sleep_for_one_second(&self)
{
let OneSecond = Duration::new(1, 0);
sleep(OneSecond);
}
#[inline(always)]
fn reconnect(&self) -> Result<(), ()>
{
self.sleep_for_one_second();
match self.socket_file_descriptor.reconnect_unix_domain_client_socket(&Self::dev_log(&self.socket_file_path))
{
Ok(()) =>
{
self.is_connected.set(true);
Ok(())
}
Err(_) =>
{
self.is_connected.set(false);
Err(())
}
}
}
#[inline(always)]
fn open(socket_file_path: &Path) -> Result<DatagramClientSocketFileDescriptor<sockaddr_un>, NewSocketClientError>
{
const send_buffer_size_socket_option: SendBufferSizeSocketOption = SendBufferSizeSocketOption
{
size: SendBufferSizeInBytes::UsualGlobalDefault,
force: false,
};
SocketFileDescriptor::new_datagram_unix_domain_socket_client(&Self::dev_log(socket_file_path), send_buffer_size_socket_option, &Blocking::default())
}
#[inline(always)]
fn dev_log(socket_file_path: &Path) -> UnixSocketAddress<&Path>
{
UnixSocketAddress::File
{
socket_file_path,
parent_folder_mode: AccessPermissions::from(0o0666u32)
}
}
}