Commit 98969dd6 authored by Geoff Simmons's avatar Geoff Simmons

The Log client can now attach to a running Varnish instance.

parent ea6c227a
......@@ -144,3 +144,24 @@ func TestAttachInstance(t *testing.T) {
"non-existent instance")
}
}
func TestPointer(t *testing.T) {
v := New()
defer v.Destroy()
p, err := v.Pointer()
if err != nil {
t.Error("Pointer():", err)
}
if p == nil {
t.Error("Pointer() returned nil")
}
var n *VSM
if _, err := n.Pointer(); err == nil {
t.Error("expected nil.Pointer() to fail")
}
uninit := new(VSM)
if _, err := uninit.Pointer(); err == nil {
t.Error("expected uninitialized.Pointer() to fail")
}
}
......@@ -41,6 +41,7 @@ import (
"errors"
"strconv"
"time"
"unsafe"
)
// An Attacher can attach to a running instance of Varnish. This means
......@@ -168,6 +169,15 @@ func (v *VSM) AttachInstance(inst string, progress bool) error {
return v.attach(progress)
}
// Pointer returns an internal pointer for use by other packages.
// Client code should never use it.
func (v *VSM) Pointer() (unsafe.Pointer, error) {
if err := v.checkNil(); err != nil {
return nil, err
}
return unsafe.Pointer(v.vsm), nil
}
/*
func (v *VSM) Get(class string, ident string) (string, error) {
var vf C.struct_vsm_fantom
......
// +build e2e
/*-
* Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
// This test is only run if go test is invoked with a -tags option
// that includes "e2e". It requires that two instances of Varnish are
// running: a default instance (varnishd invoked without the -n
// option), and an instance named "gotest" (varnishd -n "gotest").
package log
import (
"testing"
"time"
)
func TestAttach(t *testing.T) {
l := New()
defer l.Release()
if err := l.Attach(false); err != nil {
t.Fatal("Attach(false):", err)
}
var n *Log
if err := n.Attach(false); err == nil {
t.Error("expected nil.Attach() to fail")
}
uninit := new(Log)
if err := uninit.Attach(false); err == nil {
t.Error("expected uninitialized.Attach() to fail")
}
}
func TestAttachProgress(t *testing.T) {
l := New()
defer l.Release()
if err := l.Attach(true); err != nil {
t.Error("Attach(true):", err)
}
}
const zerosec = time.Duration(0)
func TestAttachTmo(t *testing.T) {
l := New()
defer l.Release()
for _, s := range []int{0, 1, -1, ^0, int(^uint(0) >> 1)} {
if e := l.AttachTmo(time.Duration(s) * time.Second); e != nil {
t.Error("AttachTmo():", e)
}
}
var n *Log
if err := n.AttachTmo(zerosec); err == nil {
t.Error("expected nil.AttachTmo() to fail")
}
uninit := new(Log)
if err := uninit.AttachTmo(zerosec); err == nil {
t.Error("expected uninitialized.AttachTmo() to fail")
}
}
func TestAttachInstance(t *testing.T) {
l := New()
defer l.Release()
if err := l.AttachInstance("gotest", false); err != nil {
t.Error("AttachInstance(\"gotest\"):", err)
}
f := New()
defer f.Release()
if err := f.AttachTmo(zerosec); err != nil {
t.Error("AttachTmo(0s):", err)
}
if err := f.AttachInstance("instanceDoesNotExist", false); err == nil {
t.Error("expected AttachInstance() to fail for a " +
"non-existent instance")
}
var n *Log
if err := n.AttachInstance("gotest", false); err == nil {
t.Error("expected nil.AttachInstance() to fail")
}
uninit := new(Log)
if err := uninit.AttachInstance("gotest", false); err == nil {
t.Error("expected uninitialized.AttachInstance() to fail")
}
}
......@@ -36,13 +36,14 @@ package log
*/
import "C"
// import "uplex.de/varnishapi/internal/pkg/vsm"
import (
"uplex.de/varnishapi/internal/pkg/vsm"
"errors"
"fmt"
"os"
"regexp"
"time"
"unsafe"
)
......@@ -337,12 +338,14 @@ type Tx struct {
// A Log instance is a client for the native logging interface.
type Log struct {
vsl *C.struct_VSL_data
cursor *C.struct_VSL_cursor
vslq *C.struct_VSLQ
grp C.enum_VSL_grouping_e
query *C.char
cbIdx int
vsl *C.struct_VSL_data
vsm *vsm.VSM
vsmopts C.unsigned
cursor *C.struct_VSL_cursor
vslq *C.struct_VSLQ
grp C.enum_VSL_grouping_e
query *C.char
cbIdx int
}
// New returns a new and initialized instance of Log. Log instances
......@@ -358,12 +361,24 @@ func New() *Log {
if log.vsl == nil {
return nil
}
log.vsm = nil
log.vsmopts = C.vsl_opt_batch() | C.vsl_opt_tail()
log.grp = C.VSL_g_vxid
log.query = nil
log.cbIdx = -1
return log
}
func (log *Log) checkNil() error {
if log == nil {
return errors.New("Log object is nil")
}
if log.vsl == nil {
return errors.New("Log object was not initialized via New()")
}
return nil
}
// Release frees native resources associated with this client. You
// should always call Release when you're done using a Log client,
// otherwise there is risk of resource leakage. It is wise to make
......@@ -391,6 +406,83 @@ func (log *Log) Error() string {
return C.GoString(C.VSL_Error(log.vsl))
}
func (log *Log) initVSM() error {
if err := log.checkNil(); err != nil {
return err
}
if log.vsm == nil {
log.vsm = vsm.New()
if log.vsm == nil {
return errors.New("Cannot initialize for attachment " +
"to shared memory")
}
}
return nil
}
// AttachTmo sets the timeout for attaching to a Varnish instance.
// The timeout tmo is rounded to seconds toward 0s. If tmo >= 0s, then
// wait that many seconds to attach. If the tmo < 0s, wait
// indefinitely. By default, the Varnish default timeout holds (5s in
// recent Varnish versions).
func (log *Log) AttachTmo(tmo time.Duration) error {
if err := log.initVSM(); err != nil {
return err
}
return log.vsm.AttachTmo(tmo)
}
func (log *Log) setCursor() error {
p, err := log.vsm.Pointer()
if err != nil {
return err
}
vsmp := (*C.struct_vsm)(p)
C.VSL_ResetError(log.vsl)
log.cursor = C.VSL_CursorVSM(log.vsl, vsmp, log.vsmopts)
if log.cursor == nil {
return log
}
return nil
}
// Attach to the default instance of Varnish -- the instance of
// varnishd invoked without the -n option.
//
// If progress is true, a dot ('.') is printed to standard output for
// each second waiting for the attach to succeed. If the attach fails
// after timeout, a newline is printed. If progress is false, there is
// no such output.
func (log *Log) Attach(progress bool) error {
if err := log.initVSM(); err != nil {
return err
}
if err := log.vsm.Attach(progress); err != nil {
return err
}
if err := log.setCursor(); err != nil {
return nil
}
return nil
}
// AttachInstance attaches to a named instance of Varnish, where name
// corresponds to the -n argument in the invocation of varnishd.
//
// The progress argument has the same meaning as for Attach.
func (log *Log) AttachInstance(name string, progress bool) error {
if err := log.initVSM(); err != nil {
return err
}
if err := log.vsm.AttachInstance(name, progress); err != nil {
return err
}
if err := log.setCursor(); err != nil {
return nil
}
return nil
}
// AttachFile attaches the client for reading from the binary log file
// located at path, as written by varnishlog -w.
//
......
......@@ -97,4 +97,22 @@ unsafe(enum VSL_tag_e tag)
return (VSL_tagflags[tag] & SLT_F_UNSAFE);
}
static inline unsigned
vsl_opt_batch()
{
return VSL_COPT_BATCH;
}
static inline unsigned
vsl_opt_tail()
{
return VSL_COPT_TAIL;
}
static inline unsigned
vsl_opt_tailstop()
{
return VSL_COPT_TAILSTOP;
}
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment