func (l *Logger) ModifyResponse(res *http.Response) error {
	ctx := martian.NewContext(res.Request)
	if ctx.SkippingLogging() {
		return nil
	}
	id := ctx.ID()
	return l.RecordResponse(id, res)
}
ModifyResponse logs responses.
package har
import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
	"unicode/utf8"
	"github.com/google/martian/v3"
	"github.com/google/martian/v3/log"
	"github.com/google/martian/v3/messageview"
	"github.com/google/martian/v3/proxyutil"
)
type Logger struct {
	bodyLogging     func(*http.Response) bool
	postDataLogging func(*http.Request) bool
	creator *Creator
	mu      sync.Mutex
	entries map[string]*Entry
	tail    *Entry
}
type HAR struct {
	Log *Log `json:"log"`
}
type Log struct {
	Version string `json:"version"`
	Creator *Creator `json:"creator"`
	Entries []*Entry `json:"entries"`
}
type Creator struct {
	Name string `json:"name"`
	Version string `json:"version"`
}
type Entry struct {
	ID string `json:"_id"`
	StartedDateTime time.Time `json:"startedDateTime"`
	Time int64 `json:"time"`
	Request *Request `json:"request"`
	Response *Response `json:"response,omitempty"`
	Cache *Cache `json:"cache"`
	Timings *Timings `json:"timings"`
	next    *Entry
}
type Request struct {
	Method string `json:"method"`
	URL string `json:"url"`
	HTTPVersion string `json:"httpVersion"`
	Cookies []Cookie `json:"cookies"`
	Headers []Header `json:"headers"`
	QueryString []QueryString `json:"queryString"`
	PostData *PostData `json:"postData,omitempty"`
	HeadersSize int64 `json:"headersSize"`
	BodySize int64 `json:"bodySize"`
}
type Response struct {
	Status int `json:"status"`
	StatusText string `json:"statusText"`
	HTTPVersion string `json:"httpVersion"`
	Cookies []Cookie `json:"cookies"`
	Headers []Header `json:"headers"`
	Content *Content `json:"content"`
	RedirectURL string `json:"redirectURL"`
	HeadersSize int64 `json:"headersSize"`
	BodySize int64 `json:"bodySize"`
}
type Cache struct {
}
type Timings struct {
	Send int64 `json:"send"`
	Wait int64 `json:"wait"`
	Receive int64 `json:"receive"`
}
type Cookie struct {
	Name string `json:"name"`
	Value string `json:"value"`
	Path string `json:"path,omitempty"`
	Domain string `json:"domain,omitempty"`
	Expires time.Time `json:"-"`
	Expires8601 string `json:"expires,omitempty"`
	HTTPOnly bool `json:"httpOnly,omitempty"`
	Secure bool `json:"secure,omitempty"`
}
type Header struct {
	Name string `json:"name"`
	Value string `json:"value"`
}
type QueryString struct {
	Name string `json:"name"`
	Value string `json:"value"`
}
type PostData struct {
	MimeType string `json:"mimeType"`
	Params []Param `json:"params"`
	Text string `json:"text"`
}
type pdBinary struct {
	MimeType string `json:"mimeType"`
	Params   []Param `json:"params"`
	Text     []byte  `json:"text"`
	Encoding string  `json:"encoding"`
}
func (p *PostData) MarshalJSON() ([]byte, error) {
	if utf8.ValidString(p.Text) {
		type noMethod PostData  
		return json.Marshal((*noMethod)(p))
	}
	return json.Marshal(pdBinary{
		MimeType: p.MimeType,
		Params:   p.Params,
		Text:     []byte(p.Text),
		Encoding: "base64",
	})
}
func (p *PostData) UnmarshalJSON(data []byte) error {
	if bytes.Equal(data, []byte("null")) {  
		return nil
	}
	var enc struct {
		Encoding string `json:"encoding"`
	}
	if err := json.Unmarshal(data, &enc); err != nil {
		return err
	}
	if enc.Encoding != "base64" {
		type noMethod PostData  
		return json.Unmarshal(data, (*noMethod)(p))
	}
	var pb pdBinary
	if err := json.Unmarshal(data, &pb); err != nil {
		return err
	}
	p.MimeType = pb.MimeType
	p.Params = pb.Params
	p.Text = string(pb.Text)
	return nil
}
type Param struct {
	Name string `json:"name"`
	Value string `json:"value,omitempty"`
	Filename string `json:"fileName,omitempty"`
	ContentType string `json:"contentType,omitempty"`
}
type Content struct {
	Size int64 `json:"size"`
	MimeType string `json:"mimeType"`
	Text []byte `json:"text,omitempty"`
	Encoding string `json:"encoding,omitempty"`
}
type contentJSON struct {
	Size     int64  `json:"size"`
	MimeType string `json:"mimeType"`
	Text string `json:"text,omitempty"`
	Encoding string `json:"encoding,omitempty"`
}
func (c Content) MarshalJSON() ([]byte, error) {
	var txt string
	switch c.Encoding {
	case "base64":
		txt = base64.StdEncoding.EncodeToString(c.Text)
	case "":
		txt = string(c.Text)
	default:
		return nil, fmt.Errorf("unsupported encoding for Content.Text: %s", c.Encoding)
	}
	cj := contentJSON{
		Size:     c.Size,
		MimeType: c.MimeType,
		Text:     txt,
		Encoding: c.Encoding,
	}
	return json.Marshal(cj)
}
func (c *Content) UnmarshalJSON(data []byte) error {
	var cj contentJSON
	if err := json.Unmarshal(data, &cj); err != nil {
		return err
	}
	var txt []byte
	var err error
	switch cj.Encoding {
	case "base64":
		txt, err = base64.StdEncoding.DecodeString(cj.Text)
		if err != nil {
			return fmt.Errorf("failed to decode base64-encoded Content.Text: %v", err)
		}
	case "":
		txt = []byte(cj.Text)
	default:
		return fmt.Errorf("unsupported encoding for Content.Text: %s", cj.Encoding)
	}
	c.Size = cj.Size
	c.MimeType = cj.MimeType
	c.Text = txt
	c.Encoding = cj.Encoding
	return nil
}
type Option func(l *Logger)
func PostDataLogging(enabled bool) Option {
	return func(l *Logger) {
		l.postDataLogging = func(*http.Request) bool {
			return enabled
		}
	}
}
func PostDataLoggingForContentTypes(cts ...string) Option {
	return func(l *Logger) {
		l.postDataLogging = func(req *http.Request) bool {
			rct := req.Header.Get("Content-Type")
			for _, ct := range cts {
				if strings.HasPrefix(strings.ToLower(rct), strings.ToLower(ct)) {
					return true
				}
			}
			return false
		}
	}
}
func SkipPostDataLoggingForContentTypes(cts ...string) Option {
	return func(l *Logger) {
		l.postDataLogging = func(req *http.Request) bool {
			rct := req.Header.Get("Content-Type")
			for _, ct := range cts {
				if strings.HasPrefix(strings.ToLower(rct), strings.ToLower(ct)) {
					return false
				}
			}
			return true
		}
	}
}
func BodyLogging(enabled bool) Option {
	return func(l *Logger) {
		l.bodyLogging = func(*http.Response) bool {
			return enabled
		}
	}
}
func BodyLoggingForContentTypes(cts ...string) Option {
	return func(l *Logger) {
		l.bodyLogging = func(res *http.Response) bool {
			rct := res.Header.Get("Content-Type")
			for _, ct := range cts {
				if strings.HasPrefix(strings.ToLower(rct), strings.ToLower(ct)) {
					return true
				}
			}
			return false
		}
	}
}
func SkipBodyLoggingForContentTypes(cts ...string) Option {
	return func(l *Logger) {
		l.bodyLogging = func(res *http.Response) bool {
			rct := res.Header.Get("Content-Type")
			for _, ct := range cts {
				if strings.HasPrefix(strings.ToLower(rct), strings.ToLower(ct)) {
					return false
				}
			}
			return true
		}
	}
}
func NewLogger() *Logger {
	l := &Logger{
		creator: &Creator{
			Name:    "martian proxy",
			Version: "2.0.0",
		},
		entries: make(map[string]*Entry),
	}
	l.SetOption(BodyLogging(true))
	l.SetOption(PostDataLogging(true))
	return l
}
func (l *Logger) SetOption(opts ...Option) {
	for _, opt := range opts {
		opt(l)
	}
}
func (l *Logger) ModifyRequest(req *http.Request) error {
	ctx := martian.NewContext(req)
	if ctx.SkippingLogging() {
		return nil
	}
	id := ctx.ID()
	return l.RecordRequest(id, req)
}
func (l *Logger) RecordRequest(id string, req *http.Request) error {
	hreq, err := NewRequest(req, l.postDataLogging(req))
	if err != nil {
		return err
	}
	entry := &Entry{
		ID:              id,
		StartedDateTime: time.Now().UTC(),
		Request:         hreq,
		Cache:           &Cache{},
		Timings:         &Timings{},
	}
	l.mu.Lock()
	defer l.mu.Unlock()
	if _, exists := l.entries[id]; exists {
		return fmt.Errorf("Duplicate request ID: %s", id)
	}
	l.entries[id] = entry
	if l.tail == nil {
		l.tail = entry
	}
	entry.next = l.tail.next
	l.tail.next = entry
	l.tail = entry
	return nil
}
func NewRequest(req *http.Request, withBody bool) (*Request, error) {
	r := &Request{
		Method:      req.Method,
		URL:         req.URL.String(),
		HTTPVersion: req.Proto,
		HeadersSize: -1,
		BodySize:    req.ContentLength,
		QueryString: []QueryString{},
		Headers:     headers(proxyutil.RequestHeader(req).Map()),
		Cookies:     cookies(req.Cookies()),
	}
	for n, vs := range req.URL.Query() {
		for _, v := range vs {
			r.QueryString = append(r.QueryString, QueryString{
				Name:  n,
				Value: v,
			})
		}
	}
	pd, err := postData(req, withBody)
	if err != nil {
		return nil, err
	}
	r.PostData = pd
	return r, nil
}