openrvdas.logger.writers.socket_writer

No module-level documentation available.
 1#!/usr/bin/env python3
 2import os
 3import socket
 4import tempfile
 5import atexit
 6import threading
 7from typing import Any, Dict
 8
 9# Add parent directory to path
10from logger.writers.writer import Writer  # noqa: E402
11
12# Global reference counter for channels
13_channel_refs: Dict[str, int] = {}
14_lock = threading.Lock()
15
16
17class SocketWriter(Writer):
18    """Writer class for socket-based IPC mechanism.
19
20    Writes records to a Unix domain socket.
21    """
22
23    def __init__(self, channel: str, **kwargs):
24        """Initialize a Writer for the specified channel.
25
26        Args:
27            channel: A string identifier for the communication channel
28        """
29        super().__init__(**kwargs)  # processes 'quiet' and type hints
30
31        # Create a unique socket path based on the channel name
32        import hashlib
33        channel_hash = hashlib.md5(channel.encode()).hexdigest()[:8]
34        self.channel = channel
35
36        # Set socket path in temp directory to avoid permission issues
37        temp_dir = tempfile.gettempdir()
38        self.socket_path = os.path.join(temp_dir, f'ipc_socket_{channel_hash}')
39
40        # Socket for sending data
41        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
42
43        # Update reference counter
44        with _lock:
45            _channel_refs[self.channel] = _channel_refs.get(self.channel, 0) + 1
46
47        # Register cleanup on exit
48        atexit.register(self.close)
49
50    def write(self, record: Any) -> bool:
51        """Write a record to the channel.
52
53        If no Reader is waiting, the record is discarded.
54
55        Args:
56            record: The data to write (will be converted to bytes)
57
58        Returns:
59            bool: True if data was written, False if it was discarded
60        """
61        if isinstance(record, str):
62            record = record.encode('utf-8')
63        elif not isinstance(record, bytes):
64            record = str(record).encode('utf-8')
65
66        # Check if the socket path exists (indicating a receiver)
67        if not os.path.exists(self.socket_path):
68            # No reader is waiting, discard the message
69            return False
70
71        # Send the record
72        try:
73            self.sock.sendto(record, self.socket_path)
74            return True
75        except (ConnectionRefusedError, FileNotFoundError):
76            # No reader is available
77            return False
78
79    def close(self):
80        """Clean up resources."""
81        if not hasattr(self, 'sock') or self.sock is None:
82            # Already closed
83            return
84
85        self.sock.close()
86        self.sock = None
87
88        # Decrement reference counter
89        with _lock:
90            _channel_refs[self.channel] = _channel_refs.get(self.channel, 1) - 1
91
92    def __del__(self):
93        """Ensure cleanup happens."""
94        self.close()
class SocketWriter(logger.writers.writer.Writer):
18class SocketWriter(Writer):
19    """Writer class for socket-based IPC mechanism.
20
21    Writes records to a Unix domain socket.
22    """
23
24    def __init__(self, channel: str, **kwargs):
25        """Initialize a Writer for the specified channel.
26
27        Args:
28            channel: A string identifier for the communication channel
29        """
30        super().__init__(**kwargs)  # processes 'quiet' and type hints
31
32        # Create a unique socket path based on the channel name
33        import hashlib
34        channel_hash = hashlib.md5(channel.encode()).hexdigest()[:8]
35        self.channel = channel
36
37        # Set socket path in temp directory to avoid permission issues
38        temp_dir = tempfile.gettempdir()
39        self.socket_path = os.path.join(temp_dir, f'ipc_socket_{channel_hash}')
40
41        # Socket for sending data
42        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
43
44        # Update reference counter
45        with _lock:
46            _channel_refs[self.channel] = _channel_refs.get(self.channel, 0) + 1
47
48        # Register cleanup on exit
49        atexit.register(self.close)
50
51    def write(self, record: Any) -> bool:
52        """Write a record to the channel.
53
54        If no Reader is waiting, the record is discarded.
55
56        Args:
57            record: The data to write (will be converted to bytes)
58
59        Returns:
60            bool: True if data was written, False if it was discarded
61        """
62        if isinstance(record, str):
63            record = record.encode('utf-8')
64        elif not isinstance(record, bytes):
65            record = str(record).encode('utf-8')
66
67        # Check if the socket path exists (indicating a receiver)
68        if not os.path.exists(self.socket_path):
69            # No reader is waiting, discard the message
70            return False
71
72        # Send the record
73        try:
74            self.sock.sendto(record, self.socket_path)
75            return True
76        except (ConnectionRefusedError, FileNotFoundError):
77            # No reader is available
78            return False
79
80    def close(self):
81        """Clean up resources."""
82        if not hasattr(self, 'sock') or self.sock is None:
83            # Already closed
84            return
85
86        self.sock.close()
87        self.sock = None
88
89        # Decrement reference counter
90        with _lock:
91            _channel_refs[self.channel] = _channel_refs.get(self.channel, 1) - 1
92
93    def __del__(self):
94        """Ensure cleanup happens."""
95        self.close()

Writer class for socket-based IPC mechanism.

Writes records to a Unix domain socket.

SocketWriter(channel: str, **kwargs)
24    def __init__(self, channel: str, **kwargs):
25        """Initialize a Writer for the specified channel.
26
27        Args:
28            channel: A string identifier for the communication channel
29        """
30        super().__init__(**kwargs)  # processes 'quiet' and type hints
31
32        # Create a unique socket path based on the channel name
33        import hashlib
34        channel_hash = hashlib.md5(channel.encode()).hexdigest()[:8]
35        self.channel = channel
36
37        # Set socket path in temp directory to avoid permission issues
38        temp_dir = tempfile.gettempdir()
39        self.socket_path = os.path.join(temp_dir, f'ipc_socket_{channel_hash}')
40
41        # Socket for sending data
42        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
43
44        # Update reference counter
45        with _lock:
46            _channel_refs[self.channel] = _channel_refs.get(self.channel, 0) + 1
47
48        # Register cleanup on exit
49        atexit.register(self.close)

Initialize a Writer for the specified channel.

Args: channel: A string identifier for the communication channel

channel
socket_path
sock
def write(self, record: Any) -> bool:
51    def write(self, record: Any) -> bool:
52        """Write a record to the channel.
53
54        If no Reader is waiting, the record is discarded.
55
56        Args:
57            record: The data to write (will be converted to bytes)
58
59        Returns:
60            bool: True if data was written, False if it was discarded
61        """
62        if isinstance(record, str):
63            record = record.encode('utf-8')
64        elif not isinstance(record, bytes):
65            record = str(record).encode('utf-8')
66
67        # Check if the socket path exists (indicating a receiver)
68        if not os.path.exists(self.socket_path):
69            # No reader is waiting, discard the message
70            return False
71
72        # Send the record
73        try:
74            self.sock.sendto(record, self.socket_path)
75            return True
76        except (ConnectionRefusedError, FileNotFoundError):
77            # No reader is available
78            return False

Write a record to the channel.

If no Reader is waiting, the record is discarded.

Args: record: The data to write (will be converted to bytes)

Returns: bool: True if data was written, False if it was discarded

def close(self):
80    def close(self):
81        """Clean up resources."""
82        if not hasattr(self, 'sock') or self.sock is None:
83            # Already closed
84            return
85
86        self.sock.close()
87        self.sock = None
88
89        # Decrement reference counter
90        with _lock:
91            _channel_refs[self.channel] = _channel_refs.get(self.channel, 1) - 1

Clean up resources.