use failure;
use failure::Fail;
use std::io::Write;
use std::net;
use std::sync;
use crate::{Result, Error, Backend, WireMessage};
pub struct TcpBackend {
socket: sync::Arc<sync::Mutex<net::TcpStream>>,
}
impl TcpBackend {
pub fn new<T: net::ToSocketAddrs>(destination: T) -> Result<TcpBackend> {
let socket = net::TcpStream::connect(destination).map_err(|e| {
failure::Error::from(e)
.context("Failed to establish TCP connection")
.context(Error::BackendCreationFailed)
})?;
socket.set_nonblocking(true).map_err(|e| {
e.context("Failed to set TcpStream to non-blocking mode")
.context(Error::BackendCreationFailed)
})?;
Ok(TcpBackend {
socket: sync::Arc::new(sync::Mutex::new(socket)),
})
}
}
impl Backend for TcpBackend {
fn log_message(&self, msg: WireMessage) -> Result<()> {
let mut msg: Vec<u8> = msg.to_gelf()?.into();
msg.push(0x00);
let mut socket = self.socket.lock().unwrap();
socket
.write_all(&msg)
.map_err(|e| e.context(Error::LogTransmitFailed))?;
Ok(())
}
}
impl Drop for TcpBackend {
fn drop(&mut self) {
let mut socket = self.socket.lock().unwrap();
socket
.flush()
.and_then(|_| socket.shutdown(net::Shutdown::Both))
.unwrap_or_else(|_| warn!("Failed to flush and shutdown tcp socket cleanly"));
}
}