Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ivans3 authored and ivans3 committed Jul 8, 2018
1 parent 03508a1 commit fc9e8d1
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM alpine:latest

COPY minikube-log-viewer /
COPY xtail /
COPY index.html /

ENTRYPOINT /minikube-log-viewer

1 change: 1 addition & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CGO_ENABLED=0 go build -a -installsuffix cgo -o minikube-log-viewer
58 changes: 58 additions & 0 deletions deployment-and-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
apiVersion: v1
kind: Service
metadata:
name: minikube-log-viewer
spec:
ports:
- port: 3000
name: http
nodePort: 32000
selector:
app: minikube-log-viewer
type: NodePort

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: minikube-log-viewer
namespace: default
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: minikube-log-viewer
spec:
volumes:
- name: logs
hostPath:
path: /var/log/containers
- name: logs-pods
hostPath:
path: /var/log/pods
#for minikube v0.22.2:
- name: logs-containers-mnt-sda1
hostPath:
path: /mnt/sda1/var/lib/docker/containers/
#for minikube v0.22.3+:
- name: logs-containers
hostPath:
path: /var/lib/docker/containers/
hostNetwork: true
containers:
- name: logviewer
image: minikube-log-viewer:latest
imagePullPolicy: Always
volumeMounts:
- name: logs
mountPath: /var/log/containers/
- name: logs-pods
mountPath: /var/log/pods
- name: logs-containers-mnt-sda1
mountPath: /mnt/sda1/var/lib/docker/containers/
- name: logs-containers
mountPath: /var/lib/docker/containers/


81 changes: 81 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<HTML>
<HEAD>
</HEAD>
<BODY BGCOLOR=BLACK>
<STYLE>
div.row {
}
span.timestamp {
font-family: "Courier New";
color: antiquewhite;
}
span.msg {
font-family: "Courier New";
font-size: 12px;
color: lightgray;
display: table-cell;
width: 100%;
word-break: break-all;
}
span.path {
font-family: "Courier New";
font-size: 12px;
color: antiquewhite;
display: table-cell;
max-width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding-right: 10px;
}
</STYLE>
<SCRIPT>
//config
var numRows = 500
//Client-side filtering:
//var fileNameMustContain = "_yournamespace_" //show only logs from containers from "yournamespace" namespace

//
var rows = []

function createRow(path, msg) {
var div = document.createElement("div");
div.className = "row"
var span1 = document.createElement("span");
span1.className = "path"
span1.innerText = path
div.appendChild(span1)
var span2 = document.createElement("span");
span2.className = "msg"
span2.innerText = msg
div.appendChild(span2)
document.body.insertBefore(div, document.getElementById("bottom"));
return div
}

if (!!window.EventSource) {
var source = new EventSource('/stream');
} else {
// Result to xhr polling :(
}

source.addEventListener('message', function(e) {
//console.log(e.data);
var obj = JSON.parse(e.data)

//if (obj.fileName.indexOf(fileNameMustContain) !== -1) { //Client-side filtering
var div = createRow(obj.fileName, obj.logObject.log)
window.scrollTo(0,document.body.scrollHeight);
rows.push(div)
if (rows.length > numRows) {
var divToDelete = rows.shift()

document.body.removeChild(divToDelete)
}
//} End Client-side filtering
}, false);
</SCRIPT>
<DIV ID=bottom></DIV>
</BODY>
</HTML>

Binary file added minikube-log-viewer
Binary file not shown.
214 changes: 214 additions & 0 deletions serve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
//
// https://gist.github.com/schmohlio/d7bdb255ba61d3f5e51a512a7c0d6a85
package main

import (
"fmt"
"log"
"net/http"
"time"
"bufio"
"os/exec"
"regexp"
"io/ioutil"
"strings"
)

// the amount of time to wait when pushing a message to
// a slow client or a client that closed after `range clients` started.
const patience time.Duration = time.Second*1

// Example SSE server in Golang.
// $ go run sse.go

//The BackLog
var backLogLength = 500
var backLog [][]byte = make([][]byte, 0, 2*backLogLength)
//Server-side filtering:
//var backLogFilenameMustContain = "_yournamespace_" //dont put system logs in the backlog

type Broker struct {

// Events are pushed to this channel by the main events-gathering routine
Notifier chan []byte

// New client connections
newClients chan chan []byte

// Closed client connections
closingClients chan chan []byte

// Client connections registry
clients map[chan []byte]bool

}

func NewServer() (broker *Broker) {
// Instantiate a broker
broker = &Broker{
Notifier: make(chan []byte, 1),
newClients: make(chan chan []byte),
closingClients: make(chan chan []byte),
clients: make(map[chan []byte]bool),
}

// Set it running - listening and broadcasting events
go broker.listen()

return
}

func (broker *Broker) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

if (req.URL.Path == "/") {
rw.Header().Set("Content-Type", "text/html")
dat, _ := ioutil.ReadFile("/index.html")
//TODO: error handling
rw.Write(dat)
return
}
if (req.URL.Path == "/debug") {
rw.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(rw, "length = %d\n", len(backLog))

return
}

// Make sure that the writer supports flushing.
//
flusher, ok := rw.(http.Flusher)

if !ok {
http.Error(rw, "Streaming unsupported!", http.StatusInternalServerError)
return
}

rw.Header().Set("Content-Type", "text/event-stream")
rw.Header().Set("Cache-Control", "no-cache")
rw.Header().Set("Connection", "keep-alive")
rw.Header().Set("Access-Control-Allow-Origin", "*")

// Each connection registers its own message channel with the Broker's connections registry
messageChan := make(chan []byte)

// Signal the broker that we have a new connection
broker.newClients <- messageChan

// Remove this client from the map of connected clients
// when this handler exits.
defer func() {
broker.closingClients <- messageChan
}()

// Listen to connection close and un-register messageChan
notify := rw.(http.CloseNotifier).CloseNotify()

//dump the backLog
for _, element := range backLog {
fmt.Fprintf(rw, "data: %s\n\n", element)
}
flusher.Flush()

for {
select {
case <-notify:
return
default:

// Write to the ResponseWriter
// Server Sent Events compatible
fmt.Fprintf(rw, "data: %s\n\n", <-messageChan)

// Flush the data immediatly instead of buffering it for later.
flusher.Flush()
}
}

}

func (broker *Broker) listen() {
for {
select {
case s := <-broker.newClients:

// A new client has connected.
// Register their message channel
broker.clients[s] = true
log.Printf("Client added. %d registered clients", len(broker.clients))
case s := <-broker.closingClients:

// A client has dettached and we want to
// stop sending them messages.
delete(broker.clients, s)
log.Printf("Removed client. %d registered clients", len(broker.clients))
case event := <-broker.Notifier:

// We got a new event from the outside!
// Send event to all connected clients
for clientMessageChan, _ := range broker.clients {
select {
case clientMessageChan <- event:
case <-time.After(patience):
log.Print("Skipping client.")
}
}
}
}

}

func main() {

broker := NewServer()

cmd := exec.Command("/xtail","/var/log/containers")

stdout, err := cmd.StdoutPipe()
checkError(err)
err = cmd.Start()
checkError(err)
defer cmd.Wait() // Doesn't block

scanner := bufio.NewScanner(stdout)

currentFile := ""
re1 := regexp.MustCompile("^\\*\\*\\* /var/log/containers/(?P<Path>.*) \\*\\*\\*$")

go func() {
for scanner.Scan() {
eventString := scanner.Text()
m1 := re1.FindStringSubmatch(eventString)
if (m1 != nil) {
currentFile = m1[1]
//log.Println("File: "+currentFile)
} else if (eventString != "") && (! strings.HasPrefix(eventString, "***")) {
jsonBytes := []byte("{\"fileName\":\""+currentFile+"\",\"logObject\":"+eventString+"}")

//log.Println("Receiving event")
broker.Notifier <- jsonBytes
//put it on the backlog
//if (strings.Contains(currentFile, backLogFilenameMustContain)) {
backLog = append(backLog, jsonBytes)
if (len(backLog) >= backLogLength) {
//shift
backLog = backLog[1:]
}
//}
} else {
//fmt.Println("Ignoring line")
}
}


}()

log.Fatal("HTTP server error: ", http.ListenAndServe(":3000", broker))

}

func checkError(err error) {
if err != nil {
log.Fatalf("Error: %s", err)
}
}

Binary file added xtail
Binary file not shown.

0 comments on commit fc9e8d1

Please sign in to comment.