1
0
mirror of https://github.com/taigrr/log-socket synced 2026-03-20 14:52:27 -07:00

5 Commits

12 changed files with 760 additions and 271 deletions

View File

@@ -4,7 +4,6 @@ on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@@ -12,7 +11,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.16'
go-version: "1.21"
- name: Install dependencies
run: go get .

View File

@@ -1,4 +1,4 @@
Copyright (C) 2019-2022 by Tai Groot <tai@taigrr.com>
Copyright (C) 2019-2025 by Tai Groot <tai@taigrr.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

View File

@@ -1,15 +1,69 @@
log-socket
==========
`log-socket` is a drop-in replacement for Go's `log` package that allows for streaming of logs via WebSockets.
# Log Socket
A real-time log viewer with WebSocket support, written in Go. This tool provides a web-based interface for viewing and filtering logs in real-time.
## Features
- Real-time log streaming via WebSocket
- Web-based log viewer with filtering capabilities
- Support for multiple log levels (TRACE, DEBUG, INFO, WARN, ERROR, PANIC, FATAL)
- Color-coded log levels for better visibility
- Auto-scrolling with toggle option
- Log download functionality
- Log clearing capability
- File source tracking for each log entry
## Installation
To install the library:
`go get github.com/taigrr/log-socket`
## Running
To run a demo of this library:
`go run main.go`
```bash
go install github.com/taigrr/log-socket@latest
```
This demo will do a sample of every log type and push results to `0.0.0.0:8080`. Once running, you can open a browser and navigate to
`0.0.0.0:8080` to see an example implementation of how logs are streamed.
## Example Preview
1. Start the server:
```bash
log-socket
```
By default, the server runs on `0.0.0.0:8080`. You can specify a different address using the `-addr` flag:
```bash
log-socket -addr localhost:8080
```
2. Open your browser and navigate to `http://localhost:8080`
![Log Socket Web Interface](browser/screenshot.png)
## Logging Interface
The package provides a comprehensive logging interface with the following methods:
- `Trace/Tracef/Traceln`: For trace-level logging
- `Debug/Debugf/Debugln`: For debug-level logging
- `Info/Infof/Infoln`: For info-level logging
- `Notice/Noticef/Noticeln`: For notice-level logging
- `Warn/Warnf/Warnln`: For warning-level logging
- `Error/Errorf/Errorln`: For error-level logging
- `Panic/Panicf/Panicln`: For panic-level logging
- `Fatal/Fatalf/Fatalln`: For fatal-level logging
## Web Interface Features
- **Filtering**: Type in the search box to filter logs
- **Auto-scroll**: Toggle auto-scrolling with the checkbox
- **Download**: Save all logs as a JSON file
- **Clear**: Remove all logs from the viewer
- **Color Coding**: Different log levels are color-coded for easy identification
## Dependencies
- [gorilla/websocket](https://github.com/gorilla/websocket) for WebSocket support
## Notes
The web interface is not meant to be used as-is.
It functions perfectly well for some scenarios, but it is broken out into a different package intentionally, such that users can add their own as they see fit.
It's mostly here to provide an example of how to consume the websocket data and display it.

View File

@@ -7,6 +7,9 @@ import (
"strings"
)
//go:embed viewer.html
var webpage string
func LogSocketViewHandler(w http.ResponseWriter, r *http.Request) {
wsResource := r.Host + r.URL.Path
if r.TLS != nil {
@@ -18,7 +21,4 @@ func LogSocketViewHandler(w http.ResponseWriter, r *http.Request) {
homeTemplate.Execute(w, wsResource)
}
//go:embed viewer.html
var webpage string
var homeTemplate = template.Must(template.New("").Parse(webpage))

BIN
browser/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

@@ -1,196 +1,630 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<center>
<input type="text" id="search" onkeyup="filterTable()" placeholder="Filter...">
<input type="checkbox" id="shouldScroll" checked>Enable Autoscroll<br>
<table id="logHeaders" style="text-align:left; width:80%;" >
<tbody>
<tr class="header">
<th style="width:20%;">TimeStamp</th>
<th style="width:5%;">Level</th>
<th style="width:65%;">Output</th>
<th style="width:10%;">Source</th>
</tr>
</tbody>
</table>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Log Viewer</title>
<style>
:root {
--primary-color: #2196F3;
--error-color: #f44336;
--warning-color: #ff9800;
--success-color: #4caf50;
--info-color: #2196F3;
--debug-color: #9c27b0;
--trace-color: #607d8b;
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--text-primary: #333333;
--text-secondary: #666666;
--border-color: #e0e0e0;
--shadow: 0 2px 4px rgba(0,0,0,0.1);
--shadow-hover: 0 4px 8px rgba(0,0,0,0.15);
}
<div id="tableWrapper">
<table id="logs" style="text-align:left; width:100%;" >
<tbody id="tbodylogs">
<tr class="header">
<th style="width:20%;"></th>
<th style="width:5%;"></th>
<th style="width:65%;"></th>
<th style="width:10%;"></th>
</tr>
* {
box-sizing: border-box;
}
</tbody>
</table>
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
background-color: var(--bg-secondary);
color: var(--text-primary);
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.header {
background: var(--bg-primary);
padding: 20px;
border-radius: 8px;
box-shadow: var(--shadow);
margin-bottom: 20px;
}
.header h1 {
margin: 0 0 20px 0;
color: var(--text-primary);
font-size: 24px;
font-weight: 600;
}
.controls {
display: flex;
gap: 15px;
align-items: center;
flex-wrap: wrap;
}
.search-container {
position: relative;
flex: 1;
min-width: 250px;
}
.search-input {
width: 100%;
padding: 10px 15px;
border: 2px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
transition: border-color 0.2s ease;
}
.search-input:focus {
outline: none;
border-color: var(--primary-color);
}
.checkbox-container {
display: flex;
align-items: center;
gap: 8px;
user-select: none;
}
.checkbox-container input[type="checkbox"] {
width: 16px;
height: 16px;
}
.checkbox-container label {
font-size: 14px;
color: var(--text-secondary);
cursor: pointer;
}
.log-container {
background: var(--bg-primary);
border-radius: 8px;
box-shadow: var(--shadow);
overflow: hidden;
margin-bottom: 20px;
}
.log-header {
background: var(--bg-secondary);
padding: 12px 0;
border-bottom: 1px solid var(--border-color);
font-weight: 600;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.log-table {
width: 100%;
border-collapse: collapse;
}
.log-row {
display: grid;
grid-template-columns: 180px 80px 1fr 120px;
gap: 15px;
padding: 10px 15px;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.2s ease;
}
.log-row:hover {
background-color: rgba(0,0,0,0.02);
}
.log-cell {
display: flex;
align-items: center;
font-size: 13px;
word-break: break-word;
}
.log-cell.timestamp {
font-family: 'Monaco', 'Menlo', monospace;
color: var(--text-secondary);
font-size: 12px;
}
.log-cell.level {
font-weight: 600;
text-align: center;
justify-content: center;
}
.log-cell.output {
font-family: 'Monaco', 'Menlo', monospace;
white-space: pre-wrap;
}
.log-cell.source {
font-size: 11px;
color: var(--text-secondary);
}
.log-level-error { background-color: #ffebee; color: var(--error-color); }
.log-level-warn { background-color: #fff3e0; color: var(--warning-color); }
.log-level-info { background-color: #e3f2fd; color: var(--info-color); }
.log-level-debug { background-color: #f3e5f5; color: var(--debug-color); }
.log-level-trace { background-color: #eceff1; color: var(--trace-color); }
.log-viewer {
height: 60vh;
overflow-y: auto;
overflow-x: hidden;
}
.log-viewer::-webkit-scrollbar {
width: 8px;
}
.log-viewer::-webkit-scrollbar-track {
background: var(--bg-secondary);
}
.log-viewer::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 4px;
}
.log-viewer::-webkit-scrollbar-thumb:hover {
background: #999;
}
.actions {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-hover);
}
.btn:active {
transform: translateY(0);
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-danger {
background-color: var(--error-color);
color: white;
}
.status-bar {
background: var(--bg-primary);
padding: 10px 20px;
border-radius: 6px;
box-shadow: var(--shadow);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: var(--text-secondary);
}
.connection-status {
display: flex;
align-items: center;
gap: 8px;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--error-color);
}
.status-indicator.connected {
background-color: var(--success-color);
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: var(--text-secondary);
}
.empty-state h3 {
margin: 0 0 10px 0;
font-size: 18px;
}
.empty-state p {
margin: 0;
font-size: 14px;
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
.controls {
flex-direction: column;
align-items: stretch;
}
.log-row {
grid-template-columns: 1fr;
gap: 5px;
}
.log-cell.timestamp::before {
content: 'Time: ';
font-weight: 600;
}
.log-cell.level::before {
content: 'Level: ';
font-weight: 600;
}
.log-cell.source::before {
content: 'Source: ';
font-weight: 600;
}
.log-header {
display: none;
}
}
</style>
</head>
<body>
<div class="container">
<header class="header">
<h1>📊 Log Viewer</h1>
<div class="controls">
<div class="search-container">
<input
type="text"
id="search"
class="search-input"
placeholder="🔍 Filter logs..."
aria-label="Filter logs"
>
</div>
<div class="checkbox-container">
<input type="checkbox" id="shouldScroll" checked>
<label for="shouldScroll">Auto-scroll</label>
</div>
</div>
<br>
<input class="button" type="button" id="download" value="Download Logs" style="background-color:#3f51b5;"/>
<input class="button" type="button" id="delete" value="Delete Logs" style="background-color:#f44336"/>
</center>
</body>
<footer>
<style>
#tableWrapper{
overflow-y: scroll;
display: flow-root;
width: 80%;
height: 80vh;
}
td,tr{
height: min-content;
}
.button{
display: inline-block;
width: 5vw;
height: 5vh;
}
</style>
<script>
var logTable = document.getElementById("logs");
var logTableB = document.getElementById("tbodylogs");
var ws = null;
var application = "demo-commit"
var logs = [];
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
function openSocket() {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onclose = async function(evt) {
ws = null;
while(ws == null){
openSocket()
await sleep(5000);
}
}
ws.onmessage = function(evt) {
var entry = JSON.parse(evt.data)
logs.push(entry)
var row = document.createElement('tr');
var ts = document.createElement('td');
var tst = document.createTextNode(entry.timestamp);
ts.appendChild(tst);
row.appendChild(ts);
var ts = document.createElement('td');
var tst = document.createTextNode(entry.level);
ts.appendChild(tst);
row.appendChild(ts);
var ts = document.createElement('td');
var tst = document.createTextNode(entry.output);
ts.appendChild(tst);
row.appendChild(ts);
var ts = document.createElement('td');
var tst = document.createTextNode(entry.file);
ts.appendChild(tst);
row.appendChild(ts);
var bg="";
switch(entry.level){
case "INFO":
bg="white";
break;
case "ERROR":
bg="#f44336";
break;
case "WARN":
bg="#fb8c00"
break;
case "TRACE":
bg="#E1F5FE"
break;
case "DEBUG":
bg="#B3E5FC"
break;
default:
bg="white"
break;
}
row.style.backgroundColor=bg
logTableB.append(row)
filterTable()
}
ws.onerror = function(evt) {
if (evt != null && evt.data != null){
// handle error here
}
}
}
function clearTable(){
if(!window.confirm("Are you sure you want to delete all logs?")){
return
}
logs = []
</header>
while (logTableB.childNodes.length > 1) {
logTableB.removeChild(logTableB.childNodes[1]);
}
<div class="log-container">
<div class="log-header">
<div class="log-row">
<div class="log-cell">Timestamp</div>
<div class="log-cell">Level</div>
<div class="log-cell">Message</div>
<div class="log-cell">Source</div>
</div>
</div>
<div id="logViewer" class="log-viewer">
<div id="emptyState" class="empty-state">
<h3>No logs yet</h3>
<p>Waiting for log entries...</p>
</div>
</div>
</div>
<div class="actions">
<button id="downloadBtn" class="btn btn-primary">
📥 Download Logs
</button>
<button id="clearBtn" class="btn btn-danger">
🗑️ Clear Logs
</button>
</div>
<div class="status-bar">
<div class="connection-status">
<span class="status-indicator" id="statusIndicator"></span>
<span id="connectionStatus">Connecting...</span>
</div>
<div id="logCount">0 logs</div>
</div>
</div>
<script>
class LogViewer {
constructor() {
this.ws = null;
this.logs = [];
this.filteredLogs = [];
this.isConnected = false;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.initializeElements();
this.attachEventListeners();
this.connectWebSocket();
this.startAutoScroll();
}
function filterTable() {
var cols, input, filter, table, tr, td, i, txtValue, w;
input = document.getElementById("search");
filter = input.value;
table = logTableB;
tr = table.getElementsByTagName("tr");
for (i = 1; i < tr.length; i++) {
cols = tr[i].getElementsByTagName("td");
var visible = false;
for (w = 0; w < cols.length; w++){
if (!visible && cols[w]) {
td = cols[w]
txtValue = td.textContent || td.innerText;
if (txtValue.indexOf(filter) > -1) {
visible = true
}
initializeElements() {
this.logViewer = document.getElementById('logViewer');
this.emptyState = document.getElementById('emptyState');
this.searchInput = document.getElementById('search');
this.scrollCheckbox = document.getElementById('shouldScroll');
this.downloadBtn = document.getElementById('downloadBtn');
this.clearBtn = document.getElementById('clearBtn');
this.statusIndicator = document.getElementById('statusIndicator');
this.connectionStatus = document.getElementById('connectionStatus');
this.logCount = document.getElementById('logCount');
}
attachEventListeners() {
this.searchInput.addEventListener('input', this.debounce(() => this.filterLogs(), 300));
this.downloadBtn.addEventListener('click', () => this.downloadLogs());
this.clearBtn.addEventListener('click', () => this.clearLogs());
}
connectWebSocket() {
if (this.ws) return;
try {
this.ws = new WebSocket("{{.}}");
this.updateConnectionStatus('Connecting...', false);
this.ws.onopen = () => {
this.isConnected = true;
this.reconnectAttempts = 0;
this.updateConnectionStatus('Connected', true);
};
this.ws.onmessage = (event) => {
try {
const logEntry = JSON.parse(event.data);
this.addLogEntry(logEntry);
} catch (error) {
console.error('Failed to parse log entry:', error);
}
}
if(visible){
};
tr[i].style.display = "";
} else {
this.ws.onclose = () => {
this.isConnected = false;
this.ws = null;
this.updateConnectionStatus('Disconnected', false);
this.scheduleReconnect();
};
tr[i].style.display = "none";
}
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
this.updateConnectionStatus('Connection Error', false);
};
} catch (error) {
console.error('Failed to create WebSocket:', error);
this.updateConnectionStatus('Connection Failed', false);
this.scheduleReconnect();
}
}
function pageScroll() {
if (document.getElementById('shouldScroll').checked) {
document.getElementById('tableWrapper').scrollBy(0,10);
scheduleReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
this.updateConnectionStatus(`Reconnecting in ${Math.ceil(delay/1000)}s...`, false);
setTimeout(() => {
this.connectWebSocket();
}, delay);
} else {
this.updateConnectionStatus('Connection Failed', false);
}
setTimeout(pageScroll,10);
}
document.getElementById("delete").addEventListener("click", function(){
addLogEntry(entry) {
this.logs.push(entry);
this.updateLogCount();
if (this.matchesFilter(entry)) {
this.renderLogEntry(entry);
this.hideEmptyState();
}
}
clearTable()
}, false);
document.getElementById("download").addEventListener("click", function(){
renderLogEntry(entry) {
const logRow = document.createElement('div');
logRow.className = `log-row log-level-${entry.level.toLowerCase()}`;
logRow.innerHTML = `
<div class="log-cell timestamp">${this.formatTimestamp(entry.timestamp)}</div>
<div class="log-cell level">${entry.level}</div>
<div class="log-cell output">${this.escapeHtml(entry.output)}</div>
<div class="log-cell source">${this.escapeHtml(entry.file || 'N/A')}</div>
`;
download(application+'.json',JSON.stringify(logs));
}, false);
openSocket();
pageScroll();
</script>
this.logViewer.appendChild(logRow);
}
</footer>
formatTimestamp(timestamp) {
try {
const date = new Date(timestamp);
return date.toLocaleString();
} catch {
return timestamp;
}
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
filterLogs() {
const query = this.searchInput.value.toLowerCase().trim();
// Clear current display
this.clearLogDisplay();
if (!query) {
// Show all logs
this.logs.forEach(log => this.renderLogEntry(log));
} else {
// Show filtered logs
const filtered = this.logs.filter(log =>
this.matchesQuery(log, query)
);
filtered.forEach(log => this.renderLogEntry(log));
}
if (this.logViewer.children.length === 0 ||
(this.logViewer.children.length === 1 && this.logViewer.contains(this.emptyState))) {
this.showEmptyState();
} else {
this.hideEmptyState();
}
}
matchesFilter(entry) {
const query = this.searchInput.value.toLowerCase().trim();
return !query || this.matchesQuery(entry, query);
}
matchesQuery(entry, query) {
return (
entry.output.toLowerCase().includes(query) ||
entry.level.toLowerCase().includes(query) ||
(entry.file && entry.file.toLowerCase().includes(query)) ||
entry.timestamp.toLowerCase().includes(query)
);
}
clearLogDisplay() {
while (this.logViewer.firstChild && this.logViewer.firstChild !== this.emptyState) {
this.logViewer.removeChild(this.logViewer.firstChild);
}
}
showEmptyState() {
if (!this.logViewer.contains(this.emptyState)) {
this.logViewer.appendChild(this.emptyState);
}
}
hideEmptyState() {
if (this.logViewer.contains(this.emptyState)) {
this.logViewer.removeChild(this.emptyState);
}
}
clearLogs() {
if (!confirm('Are you sure you want to clear all logs?')) return;
this.logs = [];
this.clearLogDisplay();
this.showEmptyState();
this.updateLogCount();
}
downloadLogs() {
if (this.logs.length === 0) {
alert('No logs to download');
return;
}
const dataStr = JSON.stringify(this.logs, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const link = document.createElement('a');
link.href = URL.createObjectURL(dataBlob);
link.download = `logs-${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
}
updateConnectionStatus(status, connected) {
this.connectionStatus.textContent = status;
this.statusIndicator.classList.toggle('connected', connected);
}
updateLogCount() {
const count = this.logs.length;
this.logCount.textContent = `${count} log${count !== 1 ? 's' : ''}`;
}
startAutoScroll() {
const autoScroll = () => {
if (this.scrollCheckbox.checked && this.isConnected) {
this.logViewer.scrollTop = this.logViewer.scrollHeight;
}
requestAnimationFrame(autoScroll);
};
autoScroll();
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
}
// Initialize the log viewer when the page loads
document.addEventListener('DOMContentLoaded', () => {
new LogViewer();
});
</script>
</body>
</html>

4
go.mod
View File

@@ -1,5 +1,5 @@
module github.com/taigrr/log-socket
go 1.21
go 1.24.4
require github.com/gorilla/websocket v1.5.0
require github.com/gorilla/websocket v1.5.3

4
go.sum
View File

@@ -1,2 +1,2 @@
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=

View File

@@ -116,7 +116,7 @@ func (c *Client) Get() Entry {
}
// Trace prints out logs on trace level
func Trace(args ...interface{}) {
func Trace(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -129,7 +129,7 @@ func Trace(args ...interface{}) {
}
// Formatted print for Trace
func Tracef(format string, args ...interface{}) {
func Tracef(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -142,7 +142,7 @@ func Tracef(format string, args ...interface{}) {
}
// Trace prints out logs on trace level with newline
func Traceln(args ...interface{}) {
func Traceln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -155,7 +155,7 @@ func Traceln(args ...interface{}) {
}
// Debug prints out logs on debug level
func Debug(args ...interface{}) {
func Debug(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -168,7 +168,7 @@ func Debug(args ...interface{}) {
}
// Formatted print for Debug
func Debugf(format string, args ...interface{}) {
func Debugf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -181,7 +181,7 @@ func Debugf(format string, args ...interface{}) {
}
// Debug prints out logs on debug level with a newline
func Debugln(args ...interface{}) {
func Debugln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -194,7 +194,7 @@ func Debugln(args ...interface{}) {
}
// Info prints out logs on info level
func Info(args ...interface{}) {
func Info(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -207,7 +207,7 @@ func Info(args ...interface{}) {
}
// Formatted print for Info
func Infof(format string, args ...interface{}) {
func Infof(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -220,7 +220,7 @@ func Infof(format string, args ...interface{}) {
}
// Info prints out logs on info level with a newline
func Infoln(args ...interface{}) {
func Infoln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -233,7 +233,7 @@ func Infoln(args ...interface{}) {
}
// Info prints out logs on info level
func Notice(args ...interface{}) {
func Notice(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -246,7 +246,7 @@ func Notice(args ...interface{}) {
}
// Formatted print for Info
func Noticef(format string, args ...interface{}) {
func Noticef(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -259,7 +259,7 @@ func Noticef(format string, args ...interface{}) {
}
// Info prints out logs on info level with a newline
func Noticeln(args ...interface{}) {
func Noticeln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -272,7 +272,7 @@ func Noticeln(args ...interface{}) {
}
// Warn prints out logs on warn level
func Warn(args ...interface{}) {
func Warn(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -285,7 +285,7 @@ func Warn(args ...interface{}) {
}
// Formatted print for Warn
func Warnf(format string, args ...interface{}) {
func Warnf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -298,7 +298,7 @@ func Warnf(format string, args ...interface{}) {
}
// Newline print for Warn
func Warnln(args ...interface{}) {
func Warnln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -311,7 +311,7 @@ func Warnln(args ...interface{}) {
}
// Error prints out logs on error level
func Error(args ...interface{}) {
func Error(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -324,7 +324,7 @@ func Error(args ...interface{}) {
}
// Formatted print for error
func Errorf(format string, args ...interface{}) {
func Errorf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -337,7 +337,7 @@ func Errorf(format string, args ...interface{}) {
}
// Error prints out logs on error level with a newline
func Errorln(args ...interface{}) {
func Errorln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -350,7 +350,7 @@ func Errorln(args ...interface{}) {
}
// Panic prints out logs on panic level
func Panic(args ...interface{}) {
func Panic(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -373,7 +373,7 @@ func Panic(args ...interface{}) {
}
// Formatted print for panic
func Panicf(format string, args ...interface{}) {
func Panicf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -395,7 +395,7 @@ func Panicf(format string, args ...interface{}) {
panic(errors.New(output))
}
func Panicln(args ...interface{}) {
func Panicln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -418,7 +418,7 @@ func Panicln(args ...interface{}) {
}
// Fatal prints out logs on fatal level
func Fatal(args ...interface{}) {
func Fatal(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -433,7 +433,7 @@ func Fatal(args ...interface{}) {
}
// Formatted print for fatal
func Fatalf(format string, args ...interface{}) {
func Fatalf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -447,7 +447,7 @@ func Fatalf(format string, args ...interface{}) {
os.Exit(1)
}
func Fatalln(args ...interface{}) {
func Fatalln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -461,15 +461,15 @@ func Fatalln(args ...interface{}) {
os.Exit(1)
}
func Print(args ...interface{}) {
func Print(args ...any) {
Info(args...)
}
func Printf(format string, args ...interface{}) {
func Printf(format string, args ...any) {
Infof(format, args...)
}
func Println(args ...interface{}) {
func Println(args ...any) {
Infoln(args...)
}

View File

@@ -16,7 +16,7 @@ func (l *Logger) SetInfoDepth(depth int) {
}
// Trace prints out logs on trace level
func (l Logger) Trace(args ...interface{}) {
func (l Logger) Trace(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -29,7 +29,7 @@ func (l Logger) Trace(args ...interface{}) {
}
// Formatted print for Trace
func (l Logger) Tracef(format string, args ...interface{}) {
func (l Logger) Tracef(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -42,7 +42,7 @@ func (l Logger) Tracef(format string, args ...interface{}) {
}
// Trace prints out logs on trace level with newline
func (l Logger) Traceln(args ...interface{}) {
func (l Logger) Traceln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -55,7 +55,7 @@ func (l Logger) Traceln(args ...interface{}) {
}
// Debug prints out logs on debug level
func (l Logger) Debug(args ...interface{}) {
func (l Logger) Debug(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -68,7 +68,7 @@ func (l Logger) Debug(args ...interface{}) {
}
// Formatted print for Debug
func (l Logger) Debugf(format string, args ...interface{}) {
func (l Logger) Debugf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -81,7 +81,7 @@ func (l Logger) Debugf(format string, args ...interface{}) {
}
// Info prints out logs on info level
func (l Logger) Info(args ...interface{}) {
func (l Logger) Info(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -94,7 +94,7 @@ func (l Logger) Info(args ...interface{}) {
}
// Formatted print for Info
func (l Logger) Infof(format string, args ...interface{}) {
func (l Logger) Infof(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -107,7 +107,7 @@ func (l Logger) Infof(format string, args ...interface{}) {
}
// Info prints out logs on info level with newline
func (l Logger) Infoln(args ...interface{}) {
func (l Logger) Infoln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -120,7 +120,7 @@ func (l Logger) Infoln(args ...interface{}) {
}
// Notice prints out logs on notice level
func (l Logger) Notice(args ...interface{}) {
func (l Logger) Notice(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -133,7 +133,7 @@ func (l Logger) Notice(args ...interface{}) {
}
// Formatted print for Notice
func (l Logger) Noticef(format string, args ...interface{}) {
func (l Logger) Noticef(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -146,7 +146,7 @@ func (l Logger) Noticef(format string, args ...interface{}) {
}
// Notice prints out logs on notice level with newline
func (l Logger) Noticeln(args ...interface{}) {
func (l Logger) Noticeln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -159,7 +159,7 @@ func (l Logger) Noticeln(args ...interface{}) {
}
// Warn prints out logs on warn level
func (l Logger) Warn(args ...interface{}) {
func (l Logger) Warn(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -172,7 +172,7 @@ func (l Logger) Warn(args ...interface{}) {
}
// Formatted print for Warn
func (l Logger) Warnf(format string, args ...interface{}) {
func (l Logger) Warnf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -185,7 +185,7 @@ func (l Logger) Warnf(format string, args ...interface{}) {
}
// Warn prints out logs on warn level with a newline
func (l Logger) Warnln(args ...interface{}) {
func (l Logger) Warnln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -198,7 +198,7 @@ func (l Logger) Warnln(args ...interface{}) {
}
// Error prints out logs on error level
func (l Logger) Error(args ...interface{}) {
func (l Logger) Error(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -211,7 +211,7 @@ func (l Logger) Error(args ...interface{}) {
}
// Formatted print for error
func (l Logger) Errorf(format string, args ...interface{}) {
func (l Logger) Errorf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -224,7 +224,7 @@ func (l Logger) Errorf(format string, args ...interface{}) {
}
// Error prints out logs on error level with a new line
func (l Logger) Errorln(args ...interface{}) {
func (l Logger) Errorln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -237,7 +237,7 @@ func (l Logger) Errorln(args ...interface{}) {
}
// Panic prints out logs on panic level
func (l Logger) Panic(args ...interface{}) {
func (l Logger) Panic(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -260,7 +260,7 @@ func (l Logger) Panic(args ...interface{}) {
}
// Formatted print for panic
func (l Logger) Panicf(format string, args ...interface{}) {
func (l Logger) Panicf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -283,7 +283,7 @@ func (l Logger) Panicf(format string, args ...interface{}) {
}
// Panic prints out logs on panic level with a newline
func (l Logger) Panicln(args ...interface{}) {
func (l Logger) Panicln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -306,7 +306,7 @@ func (l Logger) Panicln(args ...interface{}) {
}
// Fatal prints out logs on fatal level
func (l Logger) Fatal(args ...interface{}) {
func (l Logger) Fatal(args ...any) {
output := fmt.Sprint(args...)
e := Entry{
Timestamp: time.Now(),
@@ -321,7 +321,7 @@ func (l Logger) Fatal(args ...interface{}) {
}
// Formatted print for fatal
func (l Logger) Fatalf(format string, args ...interface{}) {
func (l Logger) Fatalf(format string, args ...any) {
output := fmt.Sprintf(format, args...)
e := Entry{
Timestamp: time.Now(),
@@ -336,7 +336,7 @@ func (l Logger) Fatalf(format string, args ...interface{}) {
}
// Fatal prints fatal level with a new line
func (l Logger) Fatalln(args ...interface{}) {
func (l Logger) Fatalln(args ...any) {
output := fmt.Sprintln(args...)
e := Entry{
Timestamp: time.Now(),
@@ -351,16 +351,16 @@ func (l Logger) Fatalln(args ...interface{}) {
}
// Handles print to info
func (l Logger) Print(args ...interface{}) {
func (l Logger) Print(args ...any) {
l.Info(args...)
}
// Handles formatted print to info
func (l Logger) Printf(format string, args ...interface{}) {
func (l Logger) Printf(format string, args ...any) {
l.Infof(format, args...)
}
// Handles print to info with new line
func (l Logger) Println(args ...interface{}) {
func (l Logger) Println(args ...any) {
l.Infoln(args...)
}

View File

@@ -2,9 +2,6 @@ package log
import "time"
type LogWriter chan Entry
type Level int
const (
LTrace Level = iota
LDebug
@@ -16,20 +13,23 @@ const (
LFatal
)
type Client struct {
LogLevel Level `json:"level"`
writer LogWriter
initialized bool
}
type (
LogWriter chan Entry
Level int
type Entry struct {
Timestamp time.Time `json:"timestamp"`
Output string `json:"output"`
File string `json:"file"`
Level string `json:"level"`
level Level
}
type Logger struct {
FileInfoDepth int
}
Client struct {
LogLevel Level `json:"level"`
writer LogWriter
initialized bool
}
Entry struct {
Timestamp time.Time `json:"timestamp"`
Output string `json:"output"`
File string `json:"file"`
Level string `json:"level"`
level Level
}
Logger struct {
FileInfoDepth int
}
)

View File

@@ -8,10 +8,12 @@ import (
logger "github.com/taigrr/log-socket/log"
)
// var addr = flag.String("addr", "localhost:8080", "http service address")
var upgrader = websocket.Upgrader{} // use default options
func SetUpgrader(u websocket.Upgrader) {
upgrader = u
}
func LogSocketHandler(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
@@ -28,7 +30,7 @@ func LogSocketHandler(w http.ResponseWriter, r *http.Request) {
logJSON, _ := json.Marshal(logEvent)
err = c.WriteMessage(websocket.TextMessage, logJSON)
if err != nil {
logger.Error("write:", err)
logger.Warn("write:", err)
break
}
}