update: functional log parser

This commit is contained in:
Inanc Gumus 2019-08-26 17:19:27 +03:00
parent 5215300d31
commit 51eb72bdaf
13 changed files with 372 additions and 7 deletions

View File

@ -0,0 +1,19 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import "fmt"
// dumpErrs together to simplify multiple error handling
func dumpErrs(errs []error) {
for _, err := range errs {
if err != nil {
fmt.Println("> Err:", err)
}
}
}

View File

@ -0,0 +1,38 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import "strings"
func filter(r result) bool {
return filterOrg(r)
}
func filterOrg(r result) bool {
return strings.HasSuffix(r.domain, ".org")
}
func filterBlogs(r result) bool {
return strings.HasPrefix(r.domain, "blog.")
}
// type filterFunc func(result) bool
// func filter(r result, process filterFunc) bool {
// return process(r)
// }
// func noopFilter(r result) bool {
// return true
// }
// func notUsing(filter filterFunc) filterFunc {
// return func(r result) bool {
// return !filter(r)
// }
// }

View File

@ -0,0 +1,16 @@
learngoprogramming.com 10
learngoprogramming.com 15
learngoprogramming.com 10
learngoprogramming.com 20
learngoprogramming.com 5
golang.org 40
golang.org 20
golang.org 45
golang.org 15
blog.golang.org 60
blog.golang.org 30
blog.golang.org 20
blog.golang.org 65
blog.golang.org 15
inanc.io 30
inanc.io 70

View File

@ -0,0 +1,16 @@
learngoprogramming.com 10
learngoprogramming.com 15
learngoprogramming.com 10
learngoprogramming.com 20
learngoprogramming.com
golang.org 40
golang.org 20
golang.org 45
golang.org 15
blog.golang.org 60
blog.golang.org 30
blog.golang.org 20
blog.golang.org 65
blog.golang.org 15
inanc.io 30
inanc.io 70

View File

@ -0,0 +1,16 @@
learngoprogramming.com 10
learngoprogramming.com 15
learngoprogramming.com 10
learngoprogramming.com 20
learngoprogramming.com 5
golang.org 40
golang.org 20
golang.org -50
golang.org 15
blog.golang.org 60
blog.golang.org 30
blog.golang.org 20
blog.golang.org 65
blog.golang.org 15
inanc.io 30
inanc.io 70

View File

@ -0,0 +1,16 @@
learngoprogramming.com 10
learngoprogramming.com 15
learngoprogramming.com 10
learngoprogramming.com 20
learngoprogramming.com 5
golang.org FORTY
golang.org 20
golang.org 45
golang.org 15
blog.golang.org 60
blog.golang.org 30
blog.golang.org 20
blog.golang.org 65
blog.golang.org 15
inanc.io 30
inanc.io 70

View File

@ -0,0 +1,38 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"bufio"
"os"
)
/*
fmt.Println(strings.Map(unpunct, "hello!!! HOW ARE YOU???? :))"))
fmt.Println(strings.Map(unpunct, "TIME IS UP!"))
func unpunct(r rune) rune {
if unicode.IsPunct(r) {
return -1
}
return unicode.ToLower(r)
}
*/
func main() {
p := newParser()
in := bufio.NewScanner(os.Stdin)
for in.Scan() {
r := parse(p, in.Text()) // TODO: parsed -> r
update(p, r)
}
summarize(p)
dumpErrs([]error{in.Err(), err(p)})
}

View File

@ -0,0 +1,27 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"fmt"
"sort"
"strings"
)
// summarize the parsing results
func summarize(p *parser) {
sort.Strings(p.domains)
fmt.Printf("%-30s %10s\n", "DOMAIN", "VISITS")
fmt.Println(strings.Repeat("-", 45))
for _, domain := range p.domains {
fmt.Printf("%-30s %10d\n", domain, p.sum[domain].visits)
}
fmt.Printf("\n%-30s %10d\n", "TOTAL", p.total)
}

View File

@ -0,0 +1,65 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"fmt"
"strconv"
"strings"
)
// result stores details about a log line
type result struct {
domain string
visits int
// add more metrics when needed
}
// parser keep tracks of the parsing
type parser struct {
sum map[string]result // metrics per domain
domains []string // unique domain names
total int // total visits for all domains
lines int // number of parsed lines (for the error messages)
lerr error // the last error occurred
}
// newParser creates and returns a new parser
func newParser() *parser {
return &parser{sum: make(map[string]result)}
}
// update the parsing results
func parse(p *parser, line string) (r result) {
if p.lerr != nil {
return
}
p.lines++
fields := strings.Fields(line)
if len(fields) != 2 {
p.lerr = fmt.Errorf("wrong input: %v (line #%d)", fields, p.lines)
return
}
var err error
r.domain = fields[0]
r.visits, err = strconv.Atoi(fields[1])
if r.visits < 0 || err != nil {
p.lerr = fmt.Errorf("wrong input: %q (line #%d)", fields[1], p.lines)
}
return
}
// err returns the last error encountered
func err(p *parser) error {
return p.lerr
}

View File

@ -0,0 +1,33 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
// update updates all the parsing results using the given parsing result
func update(p *parser, r result) {
if p.lerr != nil {
return
}
if !filter(r) {
return
}
// Collect the unique domains
if _, ok := p.sum[r.domain]; !ok {
p.domains = append(p.domains, r.domain)
}
// Keep track of total and per domain visits
p.total += r.visits
// create and assign a new copy of `visit`
p.sum[r.domain] = result{
domain: r.domain,
visits: r.visits + p.sum[r.domain].visits,
}
}

View File

@ -5,7 +5,7 @@ import (
"sort"
"strconv"
c "github.com/wcharczuk/go-chart"
"github.com/wcharczuk/go-chart"
)
func chartWriter(w io.Writer) outputFn {
@ -19,22 +19,22 @@ func chartWrite(w io.Writer, res []result) error {
return res[i].domain > res[j].domain
})
donut := c.DonutChart{
donut := chart.DonutChart{
Title: "Total Visits Per Domain",
TitleStyle: c.Style{
TitleStyle: chart.Style{
FontSize: 35,
Show: true,
FontColor: c.ColorAlternateGreen,
FontColor: chart.ColorAlternateGreen,
},
Width: 1920,
Height: 800,
}
for _, r := range res {
v := c.Value{
v := chart.Value{
Label: r.domain + r.page + ": " + strconv.Itoa(r.visits),
Value: float64(r.visits),
Style: c.Style{
Style: chart.Style{
FontSize: 14,
},
}
@ -42,5 +42,5 @@ func chartWrite(w io.Writer, res []result) error {
donut.Values = append(donut.Values, v)
}
return donut.Render(c.SVG, w)
return donut.Render(chart.SVG, w)
}

View File

@ -0,0 +1,28 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Printf("HasPrefix: %T\n", strings.HasPrefix)
fmt.Printf("HasSuffix: %T\n", strings.HasSuffix)
fmt.Println()
var fn func(string, string) bool
fn = strings.HasPrefix
fn = strings.HasSuffix
ok := fn("gopher", "go")
fmt.Printf("ok : %t\n", ok)
}

View File

@ -0,0 +1,53 @@
// For more tutorials: https://blog.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
package main
import (
"fmt"
"unicode"
)
func main() {
s := "Func Values Are Awesome..."
// s = strings.Map(unicode.ToUpper, s)
// s = strings.Map(unicode.ToLower, s)
// s = strings.Map(excite, s)
// s = strings.Map(calm, s)
// fmt.Printf("%T\n", strings.Map)
s = mapx(calm, s)
fmt.Println(s)
}
func mapx(mapping func(rune) rune, s string) string {
var ns []rune
for _, r := range s {
if r = mapping(r); r == -1 {
continue
}
ns = append(ns, r)
}
return string(ns)
}
func calm(r rune) rune {
if unicode.IsPunct(r) {
return -1
}
return unicode.ToLower(r)
}
func excite(r rune) rune {
if r == '.' {
return '!'
}
return unicode.ToTitle(r)
}