├── t.gif ├── README ├── pixelog.go └── LICENSE /t.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudhead/pixelog/HEAD/t.gif -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | pixelog is a simple way to track visits to a web page, 2 | which doesn't require javascript. 3 | 4 | RATIONALE 5 | 6 | When you need something simple to track static HTML 7 | page visits and don't want to pollute your site with 8 | javascript. 9 | 10 | USAGE 11 | 12 | On the page we want to track, we include an tag 13 | pointing to the tracking pixel on the pixelog server: 14 | 15 | 16 | 17 | If we want to include additional information, we simply 18 | add it to the url query string: 19 | 20 | 21 | 22 | We then start the pixelog server on pixelog.local: 23 | 24 | $ pixelog -l :80 -file "t.gif" 25 | 26 | When our page is loaded, pixelog outputs a log entry: 27 | 28 | 2013/09/01 15:38:40 184.96.4.32 /t.gif?page=home "Mozilla/5.0" http://ref.co 29 | 30 | The fields in order of appearance: date, time, remote ip, request url, user-agent, 31 | referer. 32 | 33 | COPYRIGHT 34 | 35 | (C) 2013 Alexis Sellier 36 | -------------------------------------------------------------------------------- /pixelog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io/ioutil" 6 | "log" 7 | "net" 8 | "net/http" 9 | "strconv" 10 | ) 11 | 12 | func main() { 13 | l := flag.String("l", ":8080", "listen addr") 14 | file := flag.String("file", "t.gif", "tracking pixel filename") 15 | 16 | flag.Parse() 17 | 18 | pixel, err := ioutil.ReadFile(*file) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | http.HandleFunc("/"+*file, handlePixel(pixel)) 24 | http.ListenAndServe(*l, nil) 25 | } 26 | 27 | func handlePixel(pixel []byte) http.HandlerFunc { 28 | return func(w http.ResponseWriter, r *http.Request) { 29 | referer := r.Header.Get("Referer") 30 | if referer == "" { 31 | referer = "-" 32 | } 33 | agent := r.Header.Get("User-Agent") 34 | if agent == "" { 35 | agent = "-" 36 | } 37 | remote, _, err := net.SplitHostPort(r.RemoteAddr) 38 | if err != nil { 39 | remote = "-" 40 | } 41 | log.Println(remote, r.URL.String(), strconv.QuoteToASCII(agent), referer) 42 | 43 | w.Header().Set("Cache-Control", "private, max-age=0") 44 | w.WriteHeader(http.StatusOK) 45 | w.Write(pixel) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2013 Alexis Sellier 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | --------------------------------------------------------------------------------