<commit_before>// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package globalmetrics reports metrics that are computationally heavy.
// There must be a single replica of globalmetrics server.
package globalmetrics

import (
	"context"
	"time"

	"cloud.google.com/go/spanner"

	"go.chromium.org/luci/common/tsmon/field"
	"go.chromium.org/luci/common/tsmon/metric"
	"go.chromium.org/luci/common/tsmon/types"
	"go.chromium.org/luci/server"

	"go.chromium.org/luci/resultdb/internal/cron"
	"go.chromium.org/luci/resultdb/internal/span"
)

var (
	oldestExpiredResultMetric = metric.NewInt(
		"resultdb/oldest_expired_result",
		"Unix timestamp of the earliest result not yet purged",
		nil)
	expiredResultsPendingInvocationCount = metric.NewInt(
		"resultdb/expired_results/pending_invocations",
		"Number of pending invocations where expired results were not yet purged",
		nil)

	oldestTaskMetric = metric.NewInt(
		"resultdb/task/oldest_create_time",
		"The creation Unix timestamp of the oldest task.",
		&types.MetricMetadata{Units: types.Seconds},
		field.String("type")) // tasks.Type

	taskCountMetric = metric.NewInt(
		"resultdb/task/count",
		"Current number of tasks.",
		nil,
		field.String("type")) // tasks.Type
)

// Options is global metrics server configuration.
type Options struct {
	// UpdateInterval is how often to update metrics.
	UpdateInterval time.Duration
}

// InitServer initializes a backend server.
func InitServer(srv *server.Server, opts Options) {
	interval := opts.UpdateInterval
	if interval == 0 {
		interval = 5 * time.Minute
	}

	srv.RunInBackground("resultdb.tasks", func(ctx context.Context) {
		cron.Run(ctx, interval, updateTaskMetrics)
	})

	srv.RunInBackground("resultdb.oldest_expired_result", func(ctx context.Context) {
		cron.Run(ctx, interval, updateExpiredResultsMetrics)
	})
}

func updateTaskMetrics(ctx context.Context) error {
	st := spanner.NewStatement(`
		SELECT TaskType, MIN(CreateTime), COUNT(*)
		FROM InvocationTasks
		GROUP BY TaskType
	`)

	var b span.Buffer
	return span.Query(ctx, span.Client(ctx).Single(), st, func(row *spanner.Row) error {
		var taskType string
		var minCreateTime time.Time
		var count int64
		if err := b.FromSpanner(row, &taskType, &minCreateTime, &count); err != nil {
			return err
		}
		oldestTaskMetric.Set(ctx, minCreateTime.Unix(), taskType)
		taskCountMetric.Set(ctx, count, taskType)
		return nil
	})
}

func updateExpiredResultsMetrics(ctx context.Context) error {
	switch oldest, count, err := expiredResultStats(ctx); {
	case err == span.ErrNoResults:
		return nil
	case err != nil:
		return err
	default:
		oldestExpiredResultMetric.Set(ctx, oldest.Unix())
		expiredResultsPendingInvocationCount.Set(ctx, count)
		return nil
	}
}

// expiredResultStats computes the creation time of the oldest invocation
// pending to be purged in seconds.
func expiredResultStats(ctx context.Context) (oldestResult time.Time, pendingInvocationsCount int64, err error) {
	var earliest spanner.NullTime
	st := spanner.NewStatement(`
		SELECT
			MIN(ExpectedTestResultsExpirationTime) as EarliestExpiration,
			COUNT(*) as pending_count
		FROM UNNEST(GENERATE_ARRAY(0, (
			SELECT MAX(ShardId)
			FROM Invocations@{FORCE_INDEX=InvocationsByExpectedTestResultsExpiration}
			WHERE ExpectedTestResultsExpirationTime IS NOT NULL
		))) AS TargetShard
		JOIN Invocations@{FORCE_INDEX=InvocationsByExpectedTestResultsExpiration}
			ON ShardId = TargetShard
		WHERE ExpectedTestResultsExpirationTime IS NOT NULL
			AND ExpectedTestResultsExpirationTime < CURRENT_TIMESTAMP()
	`)
	err = span.QueryFirstRow(ctx, span.Client(ctx).Single(), st, &earliest, &pendingInvocationsCount)
	oldestResult = earliest.Time
	return
}
<commit_msg>[resultdb] reset oldestTaskMetric if there is no task for a given task type<commit_after>// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package globalmetrics reports metrics that are computationally heavy.
// There must be a single replica of globalmetrics server.
package globalmetrics

import (
	"context"
	"sync"
	"time"

	"cloud.google.com/go/spanner"

	"go.chromium.org/luci/common/tsmon"
	"go.chromium.org/luci/common/tsmon/field"
	"go.chromium.org/luci/common/tsmon/metric"
	"go.chromium.org/luci/common/tsmon/types"
	"go.chromium.org/luci/server"

	"go.chromium.org/luci/resultdb/internal/cron"
	"go.chromium.org/luci/resultdb/internal/span"
)

var (
	oldestExpiredResultMetric = metric.NewInt(
		"resultdb/oldest_expired_result",
		"Unix timestamp of the earliest result not yet purged",
		nil)
	expiredResultsPendingInvocationCount = metric.NewInt(
		"resultdb/expired_results/pending_invocations",
		"Number of pending invocations where expired results were not yet purged",
		nil)

	oldestTaskMetric = metric.NewInt(
		"resultdb/task/oldest_create_time",
		"The creation Unix timestamp of the oldest task.",
		&types.MetricMetadata{Units: types.Seconds},
		field.String("type")) // tasks.Type

	taskCountMetric = metric.NewInt(
		"resultdb/task/count",
		"Current number of tasks.",
		nil,
		field.String("type")) // tasks.Type

	taskStats = &taskStatPerType{}
)

// Options is global metrics server configuration.
type Options struct {
	// UpdateInterval is how often to update metrics.
	UpdateInterval time.Duration
}

// InitServer initializes a backend server.
func InitServer(srv *server.Server, opts Options) {
	interval := opts.UpdateInterval
	if interval == 0 {
		interval = 5 * time.Minute
	}

	tsmon.RegisterCallbackIn(srv.Context, func(ctx context.Context) {
		taskStats.Range(func(taskType string, stat *taskStat) bool {
			// reset the stat of each task type so that the stat of each task type can
			// continue being reported with the default values, even if it stopped being
			// present in the output of the SQL query.
			s := stat.CloneAndReset()

			// reset the oldest task creation time if there no longer exists any tasks
			// with the type.
			if s.taskCount == 0 {
				tsmon.Store(ctx).Reset(ctx, oldestTaskMetric)
			} else {
				oldestTaskMetric.Set(ctx, s.oldestCreateTime.Unix(), taskType)
			}
			taskCountMetric.Set(ctx, s.taskCount, taskType)
			return true
		})
	})

	srv.RunInBackground("resultdb.tasks", func(ctx context.Context) {
		cron.Run(ctx, interval, updateTaskStats)
	})

	srv.RunInBackground("resultdb.oldest_expired_result", func(ctx context.Context) {
		cron.Run(ctx, interval, updateExpiredResultsMetrics)
	})
}

func updateTaskStats(ctx context.Context) error {
	st := spanner.NewStatement(`
		SELECT TaskType, MIN(CreateTime), COUNT(*)
		FROM InvocationTasks
		GROUP BY TaskType
	`)

	var b span.Buffer
	return span.Query(ctx, span.Client(ctx).Single(), st, func(row *spanner.Row) error {
		var taskType string
		var minCreateTime time.Time
		var count int64
		if err := b.FromSpanner(row, &taskType, &minCreateTime, &count); err != nil {
			return err
		}
		taskStats.Update(taskType, count, minCreateTime)
		return nil
	})
}

func updateExpiredResultsMetrics(ctx context.Context) error {
	switch oldest, count, err := expiredResultStats(ctx); {
	case err == span.ErrNoResults:
		return nil
	case err != nil:
		return err
	default:
		oldestExpiredResultMetric.Set(ctx, oldest.Unix())
		expiredResultsPendingInvocationCount.Set(ctx, count)
		return nil
	}
}

// expiredResultStats computes the creation time of the oldest invocation
// pending to be purged in seconds.
func expiredResultStats(ctx context.Context) (oldestResult time.Time, pendingInvocationsCount int64, err error) {
	var earliest spanner.NullTime
	st := spanner.NewStatement(`
		SELECT
			MIN(ExpectedTestResultsExpirationTime) as EarliestExpiration,
			COUNT(*) as pending_count
		FROM UNNEST(GENERATE_ARRAY(0, (
			SELECT MAX(ShardId)
			FROM Invocations@{FORCE_INDEX=InvocationsByExpectedTestResultsExpiration}
			WHERE ExpectedTestResultsExpirationTime IS NOT NULL
		))) AS TargetShard
		JOIN Invocations@{FORCE_INDEX=InvocationsByExpectedTestResultsExpiration}
			ON ShardId = TargetShard
		WHERE ExpectedTestResultsExpirationTime IS NOT NULL
			AND ExpectedTestResultsExpirationTime < CURRENT_TIMESTAMP()
	`)
	err = span.QueryFirstRow(ctx, span.Client(ctx).Single(), st, &earliest, &pendingInvocationsCount)
	oldestResult = earliest.Time
	return
}

type taskStatPerType struct {
	m sync.Map
}

func (t *taskStatPerType) Update(taskType string, cnt int64, oldestCreateTime time.Time) {
	stat, loaded := t.m.LoadOrStore(
		taskType, &taskStat{sync.Mutex{}, cnt, oldestCreateTime})
	if loaded {
		stat.(*taskStat).Update(cnt, oldestCreateTime)
	}
}

func (t *taskStatPerType) Range(fn func(taskType string, stat *taskStat) bool) {
	t.m.Range(func(k, v interface{}) bool { return fn(k.(string), v.(*taskStat)) })
}

type taskStat struct {
	lock             sync.Mutex
	taskCount        int64
	oldestCreateTime time.Time
}

func (s *taskStat) Update(cnt int64, oldestCreateTime time.Time) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.taskCount = cnt
	s.oldestCreateTime = oldestCreateTime
}

func (s *taskStat) CloneAndReset() *taskStat {
	s.lock.Lock()
	defer s.lock.Unlock()
	ret := &taskStat{sync.Mutex{}, s.taskCount, s.oldestCreateTime}
	s.taskCount = 0
	s.oldestCreateTime = time.Time{}
	return ret
}
<|endoftext|>


<commit_before>package store

import (
	"github.com/neelance/dom/examples/todomvc/actions"
	"github.com/neelance/dom/examples/todomvc/dispatcher"
	"github.com/neelance/dom/examples/todomvc/store/model"
	"github.com/neelance/dom/storeutil"
)

var (
	Items     []*model.Item
	Filter    model.FilterState = model.All
	Listeners                   = storeutil.NewListenerRegistry()
)

func init() {
	dispatcher.Register(onAction)
}

func ActiveItemCount() int {
	return count(false)
}

func CompletedItemCount() int {
	return count(true)
}

func count(completed bool) int {
	count := 0
	for _, item := range Items {
		if item.Completed == completed {
			count++
		}
	}
	return count
}

func onAction(action interface{}) {
	switch a := action.(type) {
	case *actions.ReplaceItems:
		Items = a.Items
		Listeners.Fire()

	case *actions.AddItem:
		Items = append(Items, &model.Item{Title: a.Title, Completed: false})
		Listeners.Fire()

	case *actions.DestroyItem:
		copy(Items[a.Index:], Items[a.Index+1:])
		Items = Items[:len(Items)-1]
		Listeners.Fire()

	case *actions.SetTitle:
		Items[a.Index].Title = a.Title
		Listeners.Fire()

	case *actions.SetCompleted:
		Items[a.Index].Completed = a.Completed
		Listeners.Fire()

	case *actions.SetAllCompleted:
		for _, item := range Items {
			item.Completed = a.Completed
		}
		Listeners.Fire()

	case *actions.ClearCompleted:
		var activeItems []*model.Item
		for _, item := range Items {
			if !item.Completed {
				activeItems = append(activeItems, item)
			}
		}
		Items = activeItems
		Listeners.Fire()

	case *actions.SetFilter:
		Filter = a.Filter
		Listeners.Fire()

	}
}
<commit_msg>todomvc example: simplify store code<commit_after>package store

import (
	"github.com/neelance/dom/examples/todomvc/actions"
	"github.com/neelance/dom/examples/todomvc/dispatcher"
	"github.com/neelance/dom/examples/todomvc/store/model"
	"github.com/neelance/dom/storeutil"
)

var (
	Items     []*model.Item
	Filter    model.FilterState = model.All
	Listeners                   = storeutil.NewListenerRegistry()
)

func init() {
	dispatcher.Register(onAction)
}

func ActiveItemCount() int {
	return count(false)
}

func CompletedItemCount() int {
	return count(true)
}

func count(completed bool) int {
	count := 0
	for _, item := range Items {
		if item.Completed == completed {
			count++
		}
	}
	return count
}

func onAction(action interface{}) {
	switch a := action.(type) {
	case *actions.ReplaceItems:
		Items = a.Items

	case *actions.AddItem:
		Items = append(Items, &model.Item{Title: a.Title, Completed: false})

	case *actions.DestroyItem:
		copy(Items[a.Index:], Items[a.Index+1:])
		Items = Items[:len(Items)-1]

	case *actions.SetTitle:
		Items[a.Index].Title = a.Title

	case *actions.SetCompleted:
		Items[a.Index].Completed = a.Completed

	case *actions.SetAllCompleted:
		for _, item := range Items {
			item.Completed = a.Completed
		}

	case *actions.ClearCompleted:
		var activeItems []*model.Item
		for _, item := range Items {
			if !item.Completed {
				activeItems = append(activeItems, item)
			}
		}
		Items = activeItems

	case *actions.SetFilter:
		Filter = a.Filter

	default:
		return // don't fire listeners
	}

	Listeners.Fire()
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"net/http"
)

type MainController struct {
}

func (mc *MainController) GetHandler(responseWriter http.ResponseWriter, request *http.Request, parameters map[string]string) {
	responseWriter.Header().Add("Content-Type", "application/json")
	fmt.Fprintf(responseWriter, `{"status":200, "message": "You reached the server"}`)
}
func (mc *MainController) GetVersionHandler(responseWriter http.ResponseWriter, request *http.Request, parameters map[string]string) {
	responseWriter.Header().Add("Content-Type", "application/json")
	fmt.Fprintf(responseWriter, `{"status":200, "version": "0.1.0.6"}`)
}

func NewMainController() MainController {
	return MainController{}
}
<commit_msg>Version 7<commit_after>package controllers

import (
	"fmt"
	"net/http"
)

type MainController struct {
}

func (mc *MainController) GetHandler(responseWriter http.ResponseWriter, request *http.Request, parameters map[string]string) {
	responseWriter.Header().Add("Content-Type", "application/json")
	fmt.Fprintf(responseWriter, `{"status":200, "message": "You reached the server"}`)
}
func (mc *MainController) GetVersionHandler(responseWriter http.ResponseWriter, request *http.Request, parameters map[string]string) {
	responseWriter.Header().Add("Content-Type", "application/json")
	fmt.Fprintf(responseWriter, `{"status":200, "version": "0.1.0.7"}`)
}

func NewMainController() MainController {
	return MainController{}
}
<|endoftext|>


<commit_before>// +build !windows,!darwin,!plan9
// TODO is there a way to simplify the above? :/

// 16 february 2014

package ui

import (
	"fmt"
	"unsafe"
)

// #include "gtk_unix.h"
// /* TODO this doesn't need to be in C; it is because an earlier version did it wrong and depended on a function (g_object_set()) that took ..., which cgo doesn't like... but is it safe to embed \0 in a Go string? */
// static inline GError *gtkProgressBarAllowArbitraryResize()
// {
// 	/* min-horizontal-bar-width is a style property; we do it through CSS */
// 	/* thanks tristan in irc.gimp.net/#gtk+ */
// 	static gchar style[] =
// 		"* {\n"
// 		"	-GtkProgressBar-min-horizontal-bar-width: 1;\n"
// 		"}\n";
// 	GtkCssProvider *provider;
// 	GError *err = NULL;
//
// 	provider = gtk_css_provider_new();
// 	if (gtk_css_provider_load_from_data(provider, style, -1, &err) == FALSE)
// 		return err;
// 	/* GDK (at least as far back as GTK+ 3.4, but officially documented as of 3.10) merges all screens into one big one, so we don't need to worry about multimonitor */
// 	/* thanks to baedert and mclasen in irc.gimp.net/#gtk+ */
// 	gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
// 		(GtkStyleProvider *) provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
// 	return NULL;
// }
import "C"

func gtk_init() error {
	var err *C.GError = nil		// redundant in Go, but let's explicitly assign it anyway

	// gtk_init_with_args() gives us error info (thanks chpe in irc.gimp.net/#gtk+)
	// TODO allow GTK+ standard command-line argument processing?
	result := C.gtk_init_with_args(nil, nil, nil, nil, nil, &err)
	if result == C.FALSE {
		return fmt.Errorf("error actually initilaizing GTK+: %s", fromgstr(err.message))
	}
	// otherwise progress bars can't be resized smaller than some predetermined size
	err = C.gtkProgressBarAllowArbitraryResize()
	if err != nil {
		defer C.g_free(C.gpointer(unsafe.Pointer(err)))
		return fmt.Errorf("error allowing ProgressBar to be arbitrarily resized: %s", fromgstr(err.message))
	}
	return nil
}

func gtk_main_quit() {
	C.gtk_main_quit()
}

func gtk_window_new() *C.GtkWidget {
	return C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL)
}

func gtk_widget_show(widget *C.GtkWidget) {
	C.gtk_widget_show_all(widget)
}

func gtk_widget_hide(widget *C.GtkWidget) {
	C.gtk_widget_hide(widget)
}

func gtk_window_set_title(window *C.GtkWidget, title string) {
	ctitle := C.CString(title)
	defer C.free(unsafe.Pointer(ctitle))
	C.gtk_window_set_title(togtkwindow(window), togstr(ctitle))
}

func gtk_window_get_title(window *C.GtkWidget) string {
	return fromgstr(C.gtk_window_get_title(togtkwindow(window)))
}

func gtk_window_resize(window *C.GtkWidget, width int, height int) {
	C.gtk_window_resize(togtkwindow(window), C.gint(width), C.gint(height))
}

func gtk_window_get_size(window *C.GtkWidget) (int, int) {
	var width, height C.gint

	C.gtk_window_get_size(togtkwindow(window), &width, &height)
	return int(width), int(height)
}

// on some themes, such as oxygen-gtk, GtkLayout draws a solid-color background, not the window background (as GtkFixed and GtkDrawingArea do)
// this CSS fixes it
// thanks to drahnr and ptomato in http://stackoverflow.com/questions/22940588/how-do-i-really-make-a-gtk-3-gtklayout-transparent-draw-theme-background
// this has now been reported to the Oyxgen maintainers (https://bugs.kde.org/show_bug.cgi?id=333983); I'm not sure if I'll remove this or not when that's fixed (only if it breaks other styles... I *think* it breaks elementary OS? need to check again)
var gtkLayoutCSS = []byte(`GtkLayout {
	background-color: transparent;
}
`)

func makeTransparent(layout *C.GtkWidget) {
	var err *C.GError = nil

	provider := C.gtk_css_provider_new()
	added := C.gtk_css_provider_load_from_data(provider,
		(*C.gchar)(unsafe.Pointer(&gtkLayoutCSS[0])), C.gssize(len(gtkLayoutCSS)), &err)
	if added == C.FALSE {
		message := fromgstr(err.message)
		panic(fmt.Errorf("error loading transparent background CSS for GtkLayout: %s", message))
	}
	C.gtk_style_context_add_provider(C.gtk_widget_get_style_context(layout),
		(*C.GtkStyleProvider)(unsafe.Pointer(provider)),
		C.GTK_STYLE_PROVIDER_PRIORITY_USER)
}

// this should allow us to resize the window arbitrarily
// thanks to Company in irc.gimp.net/#gtk+
func gtkNewWindowLayout() *C.GtkWidget {
	layout := C.gtk_layout_new(nil, nil)
	makeTransparent(layout)
	scrollarea := C.gtk_scrolled_window_new((*C.GtkAdjustment)(nil), (*C.GtkAdjustment)(nil))
	C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(scrollarea)), layout)
	// never show scrollbars; we're just doing this to allow arbitrary resizes
	C.gtk_scrolled_window_set_policy((*C.GtkScrolledWindow)(unsafe.Pointer(scrollarea)),
		C.GTK_POLICY_NEVER, C.GTK_POLICY_NEVER)
	return scrollarea
}

func gtk_container_add(container *C.GtkWidget, widget *C.GtkWidget) {
	C.gtk_container_add(togtkcontainer(container), widget)
}

func gtkAddWidgetToLayout(container *C.GtkWidget, widget *C.GtkWidget) {
	layout := C.gtk_bin_get_child((*C.GtkBin)(unsafe.Pointer(container)))
	C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(layout)), widget)
}

func gtkMoveWidgetInLayout(container *C.GtkWidget, widget *C.GtkWidget, x int, y int) {
	layout := C.gtk_bin_get_child((*C.GtkBin)(unsafe.Pointer(container)))
	C.gtk_layout_move((*C.GtkLayout)(unsafe.Pointer(layout)), widget,
		C.gint(x), C.gint(y))
}

func gtk_widget_set_size_request(widget *C.GtkWidget, width int, height int) {
	C.gtk_widget_set_size_request(widget, C.gint(width), C.gint(height))
}

func gtk_button_new() *C.GtkWidget {
	return C.gtk_button_new()
}

func gtk_button_set_label(button *C.GtkWidget, label string) {
	clabel := C.CString(label)
	defer C.free(unsafe.Pointer(clabel))
	C.gtk_button_set_label(togtkbutton(button), togstr(clabel))
}

func gtk_button_get_label(button *C.GtkWidget) string {
	return fromgstr(C.gtk_button_get_label(togtkbutton(button)))
}

func gtk_check_button_new() *C.GtkWidget {
	return C.gtk_check_button_new()
}

func gtk_toggle_button_get_active(widget *C.GtkWidget) bool {
	return fromgbool(C.gtk_toggle_button_get_active(togtktogglebutton(widget)))
}

func gtk_combo_box_text_new() *C.GtkWidget {
	return C.gtk_combo_box_text_new()
}

func gtk_combo_box_text_new_with_entry() *C.GtkWidget {
	return C.gtk_combo_box_text_new_with_entry()
}

func gtk_combo_box_text_get_active_text(widget *C.GtkWidget) string {
	return fromgstr(C.gtk_combo_box_text_get_active_text(togtkcombobox(widget)))
}

func gtk_combo_box_text_append_text(widget *C.GtkWidget, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_combo_box_text_append_text(togtkcombobox(widget), togstr(ctext))
}

func gtk_combo_box_text_insert_text(widget *C.GtkWidget, index int, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_combo_box_text_insert_text(togtkcombobox(widget), C.gint(index), togstr(ctext))
}

func gtk_combo_box_get_active(widget *C.GtkWidget) int {
	cb := (*C.GtkComboBox)(unsafe.Pointer(widget))
	return int(C.gtk_combo_box_get_active(cb))
}

func gtk_combo_box_text_remove(widget *C.GtkWidget, index int) {
	C.gtk_combo_box_text_remove(togtkcombobox(widget), C.gint(index))
}

func gtkComboBoxLen(widget *C.GtkWidget) int {
	cb := (*C.GtkComboBox)(unsafe.Pointer(widget))
	model := C.gtk_combo_box_get_model(cb)
	// this is the same as with a Listbox so
	return gtkTreeModelListLen(model)
}

func gtk_entry_new() *C.GtkWidget {
	e := C.gtk_entry_new()
	// allows the GtkEntry to be resized with the window smaller than what it thinks the size should be
	// thanks to Company in irc.gimp.net/#gtk+
	C.gtk_entry_set_width_chars(togtkentry(e), 0)
	return e
}

func gtkPasswordEntryNew() *C.GtkWidget {
	e := gtk_entry_new()
	C.gtk_entry_set_visibility(togtkentry(e), C.FALSE)
	return e
}

func gtk_entry_set_text(widget *C.GtkWidget, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_entry_set_text(togtkentry(widget), togstr(ctext))
}

func gtk_entry_get_text(widget *C.GtkWidget) string {
	return fromgstr(C.gtk_entry_get_text(togtkentry(widget)))
}

var _emptystring = [1]C.gchar{0}
var emptystring = &_emptystring[0]

func gtk_label_new() *C.GtkWidget {
	label := C.gtk_label_new(emptystring)
	C.gtk_label_set_line_wrap(togtklabel(label), C.FALSE)			// turn off line wrap
	// don't call gtk_label_set_line_wrap_mode(); there's no "wrap none" value there anyway
	C.gtk_label_set_ellipsize(togtklabel(label), C.PANGO_ELLIPSIZE_NONE)		// turn off ellipsizing; this + line wrapping above will guarantee cutoff as documented
	return label
	// TODO left-justify?
}

func gtk_label_set_text(widget *C.GtkWidget, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_label_set_text(togtklabel(widget), togstr(ctext))
}

func gtk_label_get_text(widget *C.GtkWidget) string {
	return fromgstr(C.gtk_label_get_text(togtklabel(widget)))
}

func gtk_widget_get_preferred_size(widget *C.GtkWidget) (minWidth int, minHeight int, natWidth int, natHeight int) {
	var minimum, natural C.GtkRequisition

	C.gtk_widget_get_preferred_size(widget, &minimum, &natural)
	return int(minimum.width), int(minimum.height),
		int(natural.width), int(natural.height)
}

func gtk_progress_bar_new() *C.GtkWidget {
	return C.gtk_progress_bar_new()
}

func gtk_progress_bar_set_fraction(w *C.GtkWidget, percent int) {
	p := C.gdouble(percent) / 100
	C.gtk_progress_bar_set_fraction(togtkprogressbar(w), p)
}

func gtk_progress_bar_pulse(w *C.GtkWidget) {
	C.gtk_progress_bar_pulse(togtkprogressbar(w))
}
<commit_msg>Null-terminated the GtkLayout stylesheet string. This will be handy for consolidating all the GTK+ stylesheet stuff into a single Go-side init function. (Thanks to mischief, nsf, and dsal in irc.badnik.net/#go-nuts for confirming that this is valid.)<commit_after>// +build !windows,!darwin,!plan9
// TODO is there a way to simplify the above? :/

// 16 february 2014

package ui

import (
	"fmt"
	"unsafe"
)

// #include "gtk_unix.h"
// /* TODO this doesn't need to be in C; it is because an earlier version did it wrong and depended on a function (g_object_set()) that took ..., which cgo doesn't like... but is it safe to embed \0 in a Go string? */
// static inline GError *gtkProgressBarAllowArbitraryResize()
// {
// 	/* min-horizontal-bar-width is a style property; we do it through CSS */
// 	/* thanks tristan in irc.gimp.net/#gtk+ */
// 	static gchar style[] =
// 		"* {\n"
// 		"	-GtkProgressBar-min-horizontal-bar-width: 1;\n"
// 		"}\n";
// 	GtkCssProvider *provider;
// 	GError *err = NULL;
//
// 	provider = gtk_css_provider_new();
// 	if (gtk_css_provider_load_from_data(provider, style, -1, &err) == FALSE)
// 		return err;
// 	/* GDK (at least as far back as GTK+ 3.4, but officially documented as of 3.10) merges all screens into one big one, so we don't need to worry about multimonitor */
// 	/* thanks to baedert and mclasen in irc.gimp.net/#gtk+ */
// 	gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
// 		(GtkStyleProvider *) provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
// 	return NULL;
// }
import "C"

func gtk_init() error {
	var err *C.GError = nil		// redundant in Go, but let's explicitly assign it anyway

	// gtk_init_with_args() gives us error info (thanks chpe in irc.gimp.net/#gtk+)
	// TODO allow GTK+ standard command-line argument processing?
	result := C.gtk_init_with_args(nil, nil, nil, nil, nil, &err)
	if result == C.FALSE {
		return fmt.Errorf("error actually initilaizing GTK+: %s", fromgstr(err.message))
	}
	// otherwise progress bars can't be resized smaller than some predetermined size
	err = C.gtkProgressBarAllowArbitraryResize()
	if err != nil {
		defer C.g_free(C.gpointer(unsafe.Pointer(err)))
		return fmt.Errorf("error allowing ProgressBar to be arbitrarily resized: %s", fromgstr(err.message))
	}
	return nil
}

func gtk_main_quit() {
	C.gtk_main_quit()
}

func gtk_window_new() *C.GtkWidget {
	return C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL)
}

func gtk_widget_show(widget *C.GtkWidget) {
	C.gtk_widget_show_all(widget)
}

func gtk_widget_hide(widget *C.GtkWidget) {
	C.gtk_widget_hide(widget)
}

func gtk_window_set_title(window *C.GtkWidget, title string) {
	ctitle := C.CString(title)
	defer C.free(unsafe.Pointer(ctitle))
	C.gtk_window_set_title(togtkwindow(window), togstr(ctitle))
}

func gtk_window_get_title(window *C.GtkWidget) string {
	return fromgstr(C.gtk_window_get_title(togtkwindow(window)))
}

func gtk_window_resize(window *C.GtkWidget, width int, height int) {
	C.gtk_window_resize(togtkwindow(window), C.gint(width), C.gint(height))
}

func gtk_window_get_size(window *C.GtkWidget) (int, int) {
	var width, height C.gint

	C.gtk_window_get_size(togtkwindow(window), &width, &height)
	return int(width), int(height)
}

// on some themes, such as oxygen-gtk, GtkLayout draws a solid-color background, not the window background (as GtkFixed and GtkDrawingArea do)
// this CSS fixes it
// thanks to drahnr and ptomato in http://stackoverflow.com/questions/22940588/how-do-i-really-make-a-gtk-3-gtklayout-transparent-draw-theme-background
// this has now been reported to the Oyxgen maintainers (https://bugs.kde.org/show_bug.cgi?id=333983); I'm not sure if I'll remove this or not when that's fixed (only if it breaks other styles... I *think* it breaks elementary OS? need to check again)
var gtkLayoutCSS = []byte(`GtkLayout {
	background-color: transparent;
}
` + "\000")

func makeTransparent(layout *C.GtkWidget) {
	var err *C.GError = nil

	provider := C.gtk_css_provider_new()
	added := C.gtk_css_provider_load_from_data(provider,
		(*C.gchar)(unsafe.Pointer(&gtkLayoutCSS[0])), -1, &err)
	if added == C.FALSE {
		message := fromgstr(err.message)
		panic(fmt.Errorf("error loading transparent background CSS for GtkLayout: %s", message))
	}
	C.gtk_style_context_add_provider(C.gtk_widget_get_style_context(layout),
		(*C.GtkStyleProvider)(unsafe.Pointer(provider)),
		C.GTK_STYLE_PROVIDER_PRIORITY_USER)
}

// this should allow us to resize the window arbitrarily
// thanks to Company in irc.gimp.net/#gtk+
func gtkNewWindowLayout() *C.GtkWidget {
	layout := C.gtk_layout_new(nil, nil)
	makeTransparent(layout)
	scrollarea := C.gtk_scrolled_window_new((*C.GtkAdjustment)(nil), (*C.GtkAdjustment)(nil))
	C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(scrollarea)), layout)
	// never show scrollbars; we're just doing this to allow arbitrary resizes
	C.gtk_scrolled_window_set_policy((*C.GtkScrolledWindow)(unsafe.Pointer(scrollarea)),
		C.GTK_POLICY_NEVER, C.GTK_POLICY_NEVER)
	return scrollarea
}

func gtk_container_add(container *C.GtkWidget, widget *C.GtkWidget) {
	C.gtk_container_add(togtkcontainer(container), widget)
}

func gtkAddWidgetToLayout(container *C.GtkWidget, widget *C.GtkWidget) {
	layout := C.gtk_bin_get_child((*C.GtkBin)(unsafe.Pointer(container)))
	C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(layout)), widget)
}

func gtkMoveWidgetInLayout(container *C.GtkWidget, widget *C.GtkWidget, x int, y int) {
	layout := C.gtk_bin_get_child((*C.GtkBin)(unsafe.Pointer(container)))
	C.gtk_layout_move((*C.GtkLayout)(unsafe.Pointer(layout)), widget,
		C.gint(x), C.gint(y))
}

func gtk_widget_set_size_request(widget *C.GtkWidget, width int, height int) {
	C.gtk_widget_set_size_request(widget, C.gint(width), C.gint(height))
}

func gtk_button_new() *C.GtkWidget {
	return C.gtk_button_new()
}

func gtk_button_set_label(button *C.GtkWidget, label string) {
	clabel := C.CString(label)
	defer C.free(unsafe.Pointer(clabel))
	C.gtk_button_set_label(togtkbutton(button), togstr(clabel))
}

func gtk_button_get_label(button *C.GtkWidget) string {
	return fromgstr(C.gtk_button_get_label(togtkbutton(button)))
}

func gtk_check_button_new() *C.GtkWidget {
	return C.gtk_check_button_new()
}

func gtk_toggle_button_get_active(widget *C.GtkWidget) bool {
	return fromgbool(C.gtk_toggle_button_get_active(togtktogglebutton(widget)))
}

func gtk_combo_box_text_new() *C.GtkWidget {
	return C.gtk_combo_box_text_new()
}

func gtk_combo_box_text_new_with_entry() *C.GtkWidget {
	return C.gtk_combo_box_text_new_with_entry()
}

func gtk_combo_box_text_get_active_text(widget *C.GtkWidget) string {
	return fromgstr(C.gtk_combo_box_text_get_active_text(togtkcombobox(widget)))
}

func gtk_combo_box_text_append_text(widget *C.GtkWidget, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_combo_box_text_append_text(togtkcombobox(widget), togstr(ctext))
}

func gtk_combo_box_text_insert_text(widget *C.GtkWidget, index int, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_combo_box_text_insert_text(togtkcombobox(widget), C.gint(index), togstr(ctext))
}

func gtk_combo_box_get_active(widget *C.GtkWidget) int {
	cb := (*C.GtkComboBox)(unsafe.Pointer(widget))
	return int(C.gtk_combo_box_get_active(cb))
}

func gtk_combo_box_text_remove(widget *C.GtkWidget, index int) {
	C.gtk_combo_box_text_remove(togtkcombobox(widget), C.gint(index))
}

func gtkComboBoxLen(widget *C.GtkWidget) int {
	cb := (*C.GtkComboBox)(unsafe.Pointer(widget))
	model := C.gtk_combo_box_get_model(cb)
	// this is the same as with a Listbox so
	return gtkTreeModelListLen(model)
}

func gtk_entry_new() *C.GtkWidget {
	e := C.gtk_entry_new()
	// allows the GtkEntry to be resized with the window smaller than what it thinks the size should be
	// thanks to Company in irc.gimp.net/#gtk+
	C.gtk_entry_set_width_chars(togtkentry(e), 0)
	return e
}

func gtkPasswordEntryNew() *C.GtkWidget {
	e := gtk_entry_new()
	C.gtk_entry_set_visibility(togtkentry(e), C.FALSE)
	return e
}

func gtk_entry_set_text(widget *C.GtkWidget, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_entry_set_text(togtkentry(widget), togstr(ctext))
}

func gtk_entry_get_text(widget *C.GtkWidget) string {
	return fromgstr(C.gtk_entry_get_text(togtkentry(widget)))
}

var _emptystring = [1]C.gchar{0}
var emptystring = &_emptystring[0]

func gtk_label_new() *C.GtkWidget {
	label := C.gtk_label_new(emptystring)
	C.gtk_label_set_line_wrap(togtklabel(label), C.FALSE)			// turn off line wrap
	// don't call gtk_label_set_line_wrap_mode(); there's no "wrap none" value there anyway
	C.gtk_label_set_ellipsize(togtklabel(label), C.PANGO_ELLIPSIZE_NONE)		// turn off ellipsizing; this + line wrapping above will guarantee cutoff as documented
	return label
	// TODO left-justify?
}

func gtk_label_set_text(widget *C.GtkWidget, text string) {
	ctext := C.CString(text)
	defer C.free(unsafe.Pointer(ctext))
	C.gtk_label_set_text(togtklabel(widget), togstr(ctext))
}

func gtk_label_get_text(widget *C.GtkWidget) string {
	return fromgstr(C.gtk_label_get_text(togtklabel(widget)))
}

func gtk_widget_get_preferred_size(widget *C.GtkWidget) (minWidth int, minHeight int, natWidth int, natHeight int) {
	var minimum, natural C.GtkRequisition

	C.gtk_widget_get_preferred_size(widget, &minimum, &natural)
	return int(minimum.width), int(minimum.height),
		int(natural.width), int(natural.height)
}

func gtk_progress_bar_new() *C.GtkWidget {
	return C.gtk_progress_bar_new()
}

func gtk_progress_bar_set_fraction(w *C.GtkWidget, percent int) {
	p := C.gdouble(percent) / 100
	C.gtk_progress_bar_set_fraction(togtkprogressbar(w), p)
}

func gtk_progress_bar_pulse(w *C.GtkWidget) {
	C.gtk_progress_bar_pulse(togtkprogressbar(w))
}
<|endoftext|>


<commit_before>package ln

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/ungerik/go-cairo"
)

type Path []Vector

func (p Path) BoundingBox() Box {
	box := Box{p[0], p[0]}
	for _, v := range p {
		box = box.Extend(Box{v, v})
	}
	return box
}

func (p Path) Transform(matrix Matrix) Path {
	var result Path
	for _, v := range p {
		result = append(result, matrix.MulPosition(v))
	}
	return result
}

func (p Path) Chop(step float64) Path {
	var result Path
	for i := 0; i < len(p)-1; i++ {
		a := p[i]
		b := p[i+1]
		v := b.Sub(a)
		l := v.Length()
		if i == 0 {
			result = append(result, a)
		}
		d := step
		for d < l {
			result = append(result, a.Add(v.MulScalar(d/l)))
			d += step
		}
		result = append(result, b)
	}
	return result
}

func (p Path) Filter(f Filter) Paths {
	var result Paths
	var path Path
	for _, v := range p {
		v, ok := f.Filter(v)
		// ok = ok || i%8 < 4 // show hidden lines
		if ok {
			path = append(path, v)
		} else {
			if len(path) > 1 {
				result = append(result, path)
				path = nil
			}
		}
	}
	if len(path) > 1 {
		result = append(result, path)
	}
	return result
}

func (p Path) Simplify(threshold float64) Path {
	if len(p) < 3 {
		return p
	}
	a := p[0]
	b := p[len(p)-1]
	index := -1
	distance := 0.0
	for i := 1; i < len(p)-1; i++ {
		d := p[i].SegmentDistance(a, b)
		if d > distance {
			index = i
			distance = d
		}
	}
	if distance > threshold {
		r1 := p[:index+1].Simplify(threshold)
		r2 := p[index:].Simplify(threshold)
		return append(r1[:len(r1)-1], r2...)
	} else {
		return Path{a, b}
	}
}

func (p Path) Print() {
	for _, v := range p {
		fmt.Printf("%g,%g;", v.X, v.Y)
	}
	fmt.Println()
}

func (p Path) ToSVG() string {
	var coords []string
	for _, v := range p {
		coords = append(coords, fmt.Sprintf("%f,%f", v.X, v.Y))
	}
	points := strings.Join(coords, " ")
	return fmt.Sprintf("<polyline stroke=\"black\" fill=\"none\" points=\"%s\" />", points)
}

type Paths []Path

func (p Paths) BoundingBox() Box {
	box := p[0].BoundingBox()
	for _, path := range p {
		box = box.Extend(path.BoundingBox())
	}
	return box
}

func (p Paths) Transform(matrix Matrix) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Transform(matrix))
	}
	return result
}

func (p Paths) Chop(step float64) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Chop(step))
	}
	return result
}

func (p Paths) Filter(f Filter) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Filter(f)...)
	}
	return result
}

func (p Paths) Simplify(threshold float64) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Simplify(threshold))
	}
	return result
}

func (p Paths) Print() {
	for _, path := range p {
		path.Print()
	}
}

func (p Paths) ToCairo(width, height, scale float64) *cairo.Surface {
	dc := cairo.NewSurface(cairo.FORMAT_ARGB32, int(width*scale), int(height*scale))
	dc.SetLineCap(cairo.LINE_CAP_ROUND)
	dc.SetLineJoin(cairo.LINE_JOIN_ROUND)
	dc.SetLineWidth(3)
	dc.Scale(1, -1)
	dc.Translate(0, -height*scale)
	dc.SetSourceRGB(1, 1, 1)
	dc.Paint()
	dc.SetSourceRGB(0, 0, 0)
	for _, path := range p {
		dc.NewSubPath()
		for _, v := range path {
			dc.LineTo(v.X*scale, v.Y*scale)
		}
	}
	dc.Stroke()
	return dc
}

func (p Paths) WriteToPNG(path string, width, height float64) {
	dc := p.ToCairo(width, height, 1)
	dc.WriteToPNG(path)
}

func (p Paths) ToSVG(width, height float64) string {
	var lines []string
	lines = append(lines, fmt.Sprintf("<svg width=\"%f\" height=\"%f\" version=\"1.1\" baseProfile=\"full\" xmlns=\"http://www.w3.org/2000/svg\">", width, height))
	lines = append(lines, fmt.Sprintf("<g transform=\"translate(0,%f) scale(1,-1)\">", height))
	for _, path := range p {
		lines = append(lines, path.ToSVG())
	}
	lines = append(lines, "</g></svg>")
	return strings.Join(lines, "\n")
}

func (p Paths) WriteToSVG(path string, width, height float64) error {
	return ioutil.WriteFile(path, []byte(p.ToSVG(width, height)), 0644)
}
<commit_msg>bug fix<commit_after>package ln

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/ungerik/go-cairo"
)

type Path []Vector

func (p Path) BoundingBox() Box {
	box := Box{p[0], p[0]}
	for _, v := range p {
		box = box.Extend(Box{v, v})
	}
	return box
}

func (p Path) Transform(matrix Matrix) Path {
	var result Path
	for _, v := range p {
		result = append(result, matrix.MulPosition(v))
	}
	return result
}

func (p Path) Chop(step float64) Path {
	var result Path
	for i := 0; i < len(p)-1; i++ {
		a := p[i]
		b := p[i+1]
		v := b.Sub(a)
		l := v.Length()
		if i == 0 {
			result = append(result, a)
		}
		d := step
		for d < l {
			result = append(result, a.Add(v.MulScalar(d/l)))
			d += step
		}
		result = append(result, b)
	}
	return result
}

func (p Path) Filter(f Filter) Paths {
	var result Paths
	var path Path
	for _, v := range p {
		v, ok := f.Filter(v)
		// ok = ok || i%8 < 4 // show hidden lines
		if ok {
			path = append(path, v)
		} else {
			if len(path) > 1 {
				result = append(result, path)
			}
			path = nil
		}
	}
	if len(path) > 1 {
		result = append(result, path)
	}
	return result
}

func (p Path) Simplify(threshold float64) Path {
	if len(p) < 3 {
		return p
	}
	a := p[0]
	b := p[len(p)-1]
	index := -1
	distance := 0.0
	for i := 1; i < len(p)-1; i++ {
		d := p[i].SegmentDistance(a, b)
		if d > distance {
			index = i
			distance = d
		}
	}
	if distance > threshold {
		r1 := p[:index+1].Simplify(threshold)
		r2 := p[index:].Simplify(threshold)
		return append(r1[:len(r1)-1], r2...)
	} else {
		return Path{a, b}
	}
}

func (p Path) Print() {
	for _, v := range p {
		fmt.Printf("%g,%g;", v.X, v.Y)
	}
	fmt.Println()
}

func (p Path) ToSVG() string {
	var coords []string
	for _, v := range p {
		coords = append(coords, fmt.Sprintf("%f,%f", v.X, v.Y))
	}
	points := strings.Join(coords, " ")
	return fmt.Sprintf("<polyline stroke=\"black\" fill=\"none\" points=\"%s\" />", points)
}

type Paths []Path

func (p Paths) BoundingBox() Box {
	box := p[0].BoundingBox()
	for _, path := range p {
		box = box.Extend(path.BoundingBox())
	}
	return box
}

func (p Paths) Transform(matrix Matrix) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Transform(matrix))
	}
	return result
}

func (p Paths) Chop(step float64) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Chop(step))
	}
	return result
}

func (p Paths) Filter(f Filter) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Filter(f)...)
	}
	return result
}

func (p Paths) Simplify(threshold float64) Paths {
	var result Paths
	for _, path := range p {
		result = append(result, path.Simplify(threshold))
	}
	return result
}

func (p Paths) Print() {
	for _, path := range p {
		path.Print()
	}
}

func (p Paths) ToCairo(width, height, scale float64) *cairo.Surface {
	dc := cairo.NewSurface(cairo.FORMAT_ARGB32, int(width*scale), int(height*scale))
	dc.SetLineCap(cairo.LINE_CAP_ROUND)
	dc.SetLineJoin(cairo.LINE_JOIN_ROUND)
	dc.SetLineWidth(3)
	dc.Scale(1, -1)
	dc.Translate(0, -height*scale)
	dc.SetSourceRGB(1, 1, 1)
	dc.Paint()
	dc.SetSourceRGB(0, 0, 0)
	for _, path := range p {
		dc.NewSubPath()
		for _, v := range path {
			dc.LineTo(v.X*scale, v.Y*scale)
		}
	}
	dc.Stroke()
	return dc
}

func (p Paths) WriteToPNG(path string, width, height float64) {
	dc := p.ToCairo(width, height, 1)
	dc.WriteToPNG(path)
}

func (p Paths) ToSVG(width, height float64) string {
	var lines []string
	lines = append(lines, fmt.Sprintf("<svg width=\"%f\" height=\"%f\" version=\"1.1\" baseProfile=\"full\" xmlns=\"http://www.w3.org/2000/svg\">", width, height))
	lines = append(lines, fmt.Sprintf("<g transform=\"translate(0,%f) scale(1,-1)\">", height))
	for _, path := range p {
		lines = append(lines, path.ToSVG())
	}
	lines = append(lines, "</g></svg>")
	return strings.Join(lines, "\n")
}

func (p Paths) WriteToSVG(path string, width, height float64) error {
	return ioutil.WriteFile(path, []byte(p.ToSVG(width, height)), 0644)
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package common

// Conf is responsible for defining services. Its fields
// represent elements of a service configuration.
type Conf struct {
	// Desc is the init service's description.
	Desc string

	// Env holds the environment variables that will be set when the
	// command runs.
	// Currently not used on Windows
	Env map[string]string

	// Limit holds the ulimit values that will be set when the command runs.
	// Currently not used on Windows
	Limit map[string]string

	// TODO(ericsnow) Cmd -> ExecStart.

	// Cmd is the command (with arguments) that will be run.
	// The command will be restarted if it exits with a non-zero exit code.
	Cmd string

	// Out, if set, will redirect output to that path.
	Out string

	// TODO(ericsnow) Eliminate InitDir.

	// InitDir is the folder in which the init script should be written
	// defaults to "/etc/init" on Ubuntu
	// Currently not used on Windows
	InitDir string

	// TODO(ericsnow) Turn ExtraScript into ExecStartPre.

	// ExtraScript allows to insert script before command execution
	ExtraScript string
}
<commit_msg>Add Conf.Validate.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package common

import (
	"github.com/juju/errors"
)

// Conf is responsible for defining services. Its fields
// represent elements of a service configuration.
type Conf struct {
	// Desc is the init service's description.
	Desc string

	// Env holds the environment variables that will be set when the
	// command runs.
	// Currently not used on Windows
	Env map[string]string

	// Limit holds the ulimit values that will be set when the command runs.
	// Currently not used on Windows
	Limit map[string]string

	// TODO(ericsnow) Cmd -> ExecStart.

	// Cmd is the command (with arguments) that will be run.
	// The command will be restarted if it exits with a non-zero exit code.
	Cmd string

	// Out, if set, will redirect output to that path.
	Out string

	// TODO(ericsnow) Eliminate InitDir.

	// InitDir is the folder in which the init script should be written
	// defaults to "/etc/init" on Ubuntu
	// Currently not used on Windows
	InitDir string

	// TODO(ericsnow) Turn ExtraScript into ExecStartPre.

	// ExtraScript allows to insert script before command execution
	ExtraScript string
}

// Validate checks the conf's values for correctness.
func (c Conf) Validate() error {
	if c.Desc == "" {
		return errors.New("missing Description")
	}

	if c.Cmd == "" {
		return errors.New("missing Cmd")
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package log exports logging primitives that log to stderr and also to Google Cloud Logging.
package log

// We call this log instead of logging for two reasons:
// 1) It's shorter to type;
// 2) it mimics Go's log package and can be used as a drop-in replacement for it.

import (
	"fmt"
	goLog "log"
	"os"

	"golang.org/x/net/context"
	"google.golang.org/cloud"
	"google.golang.org/cloud/logging"
)

// Logger is the interface for logging messages.
type Logger interface {
	// Printf writes a formated message to the log.
	Printf(format string, v ...interface{})

	// Print writes a message to the log.
	Print(v ...interface{})

	// Println writes a line to the log.
	Println(v ...interface{})

	// Fatal writes a message to the log and aborts.
	Fatal(v ...interface{})

	// Fatalf writes a formated message to the log and aborts.
	Fatalf(format string, v ...interface{})
}

// level represents the level of logging.
type level int

// Different levels of logging.
const (
	debug level = iota
	info
	errors
	disabled
)

// Pre-allocated Loggers at each logging level.
var (
	Debug = newLogger(debug, logging.Debug)
	Info  = newLogger(info, logging.Info)
	Error = newLogger(errors, logging.Error)

	currentLevel  level
	client        *logging.Client
	defaultLogger Logger = goLog.New(os.Stderr, "", goLog.Ldate|goLog.Ltime|goLog.LUTC|goLog.Lmicroseconds)
)

type logger struct {
	level      level
	cloudLevel logging.Level
}

var _ Logger = (*logger)(nil)

// Printf writes a formated message to the log.
func (l *logger) Printf(format string, v ...interface{}) {
	if l.level < currentLevel {
		return // Don't log at lower levels.
	}
	if client != nil {
		client.Logger(l.cloudLevel).Printf(format, v...)
	}
	defaultLogger.Printf(format, v...)
}

// Print writes a message to the log.
func (l *logger) Print(v ...interface{}) {
	if l.level < currentLevel {
		return // Don't log at lower levels.
	}
	if client != nil {
		client.Logger(l.cloudLevel).Print(v...)
	}
	defaultLogger.Print(v...)
}

// Println writes a line to the log.
func (l *logger) Println(v ...interface{}) {
	if l.level < currentLevel {
		return // Don't log at lower levels.
	}
	if client != nil {
		client.Logger(l.cloudLevel).Println(v...)
	}
	defaultLogger.Println(v...)
}

// Fatal writes a message to the log and aborts, regardless of the current log level.
func (l *logger) Fatal(v ...interface{}) {
	if client != nil {
		client.Logger(l.cloudLevel).Print(v...)
	}
	defaultLogger.Fatal(v...)
}

// Fatalf writes a formated message to the log and aborts, regardless of the current log level.
func (l *logger) Fatalf(format string, v ...interface{}) {
	if client != nil {
		client.Logger(l.cloudLevel).Printf(format, v...)
	}
	defaultLogger.Fatalf(format, v...)
}

// String returns the name of the logger.
func (l *logger) String() string {
	return toString(l.level)
}

func toString(level level) string {
	switch level {
	case info:
		return "info"
	case debug:
		return "debug"
	case errors:
		return "error"
	case disabled:
		return "disabled"
	}
	return "unknown"
}

// Level returns the current logging level.
func Level() string {
	return toString(currentLevel)
}

func toLevel(level string) (level, error) {
	switch level {
	case "info":
		return info, nil
	case "debug":
		return debug, nil
	case "error":
		return errors, nil
	case "disabled":
		return disabled, nil
	}
	return disabled, fmt.Errorf("invalid log level %q", level)
}

// SetLevel sets the current level of logging.
func SetLevel(level string) error {
	l, err := toLevel(level)
	if err != nil {
		return err
	}
	currentLevel = l
	return nil
}

// At returns whether the level will be logged currently.
func At(level string) bool {
	l, err := toLevel(level)
	if err != nil {
		return false
	}
	return currentLevel <= l
}

// Printf writes a formated message to the log.
func Printf(format string, v ...interface{}) {
	Info.Printf(format, v...)
}

// Print writes a message to the log.
func Print(v ...interface{}) {
	Info.Print(v...)
}

// Println writes a line to the log.
func Println(v ...interface{}) {
	Info.Println(v...)
}

// Fatal writes a message to the log and aborts.
func Fatal(v ...interface{}) {
	Info.Fatal(v...)
}

// Fatalf writes a formated message to the log and aborts.
func Fatalf(format string, v ...interface{}) {
	Info.Fatalf(format, v...)
}

// Connect connects all non-custom loggers (those not created by New) in this address space to a GCP Logging
// instance writing to a given logName.
func Connect(projectID, logName string) error {
	var err error
	client, err = newClient(projectID, logName)
	if err != nil {
		return err
	}
	return nil
}

// newClient creates a new client connected to the GCP Logging API with an assigned logName.
func newClient(projectID, logName string) (*logging.Client, error) {
	client, err := logging.NewClient(context.Background(), projectID, logName, cloud.WithScopes(logging.Scope))
	if err != nil {
		return nil, err
	}
	return client, nil
}

// newLogger instantiates an implicit Logger using the default client.
func newLogger(level level, cloudLevel logging.Level) Logger {
	return &logger{
		level:      level,
		cloudLevel: cloudLevel,
	}
}
<commit_msg>log: set default logging level to info, was debug<commit_after>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package log exports logging primitives that log to stderr and also to Google Cloud Logging.
package log

// We call this log instead of logging for two reasons:
// 1) It's shorter to type;
// 2) it mimics Go's log package and can be used as a drop-in replacement for it.

import (
	"fmt"
	goLog "log"
	"os"

	"golang.org/x/net/context"
	"google.golang.org/cloud"
	"google.golang.org/cloud/logging"
)

// Logger is the interface for logging messages.
type Logger interface {
	// Printf writes a formated message to the log.
	Printf(format string, v ...interface{})

	// Print writes a message to the log.
	Print(v ...interface{})

	// Println writes a line to the log.
	Println(v ...interface{})

	// Fatal writes a message to the log and aborts.
	Fatal(v ...interface{})

	// Fatalf writes a formated message to the log and aborts.
	Fatalf(format string, v ...interface{})
}

// level represents the level of logging.
type level int

// Different levels of logging.
const (
	debug level = iota
	info
	errors
	disabled
)

// Pre-allocated Loggers at each logging level.
var (
	Debug = newLogger(debug, logging.Debug)
	Info  = newLogger(info, logging.Info)
	Error = newLogger(errors, logging.Error)

	currentLevel  = info
	client        *logging.Client
	defaultLogger Logger = goLog.New(os.Stderr, "", goLog.Ldate|goLog.Ltime|goLog.LUTC|goLog.Lmicroseconds)
)

type logger struct {
	level      level
	cloudLevel logging.Level
}

var _ Logger = (*logger)(nil)

// Printf writes a formated message to the log.
func (l *logger) Printf(format string, v ...interface{}) {
	if l.level < currentLevel {
		return // Don't log at lower levels.
	}
	if client != nil {
		client.Logger(l.cloudLevel).Printf(format, v...)
	}
	defaultLogger.Printf(format, v...)
}

// Print writes a message to the log.
func (l *logger) Print(v ...interface{}) {
	if l.level < currentLevel {
		return // Don't log at lower levels.
	}
	if client != nil {
		client.Logger(l.cloudLevel).Print(v...)
	}
	defaultLogger.Print(v...)
}

// Println writes a line to the log.
func (l *logger) Println(v ...interface{}) {
	if l.level < currentLevel {
		return // Don't log at lower levels.
	}
	if client != nil {
		client.Logger(l.cloudLevel).Println(v...)
	}
	defaultLogger.Println(v...)
}

// Fatal writes a message to the log and aborts, regardless of the current log level.
func (l *logger) Fatal(v ...interface{}) {
	if client != nil {
		client.Logger(l.cloudLevel).Print(v...)
	}
	defaultLogger.Fatal(v...)
}

// Fatalf writes a formated message to the log and aborts, regardless of the current log level.
func (l *logger) Fatalf(format string, v ...interface{}) {
	if client != nil {
		client.Logger(l.cloudLevel).Printf(format, v...)
	}
	defaultLogger.Fatalf(format, v...)
}

// String returns the name of the logger.
func (l *logger) String() string {
	return toString(l.level)
}

func toString(level level) string {
	switch level {
	case info:
		return "info"
	case debug:
		return "debug"
	case errors:
		return "error"
	case disabled:
		return "disabled"
	}
	return "unknown"
}

// Level returns the current logging level.
func Level() string {
	return toString(currentLevel)
}

func toLevel(level string) (level, error) {
	switch level {
	case "info":
		return info, nil
	case "debug":
		return debug, nil
	case "error":
		return errors, nil
	case "disabled":
		return disabled, nil
	}
	return disabled, fmt.Errorf("invalid log level %q", level)
}

// SetLevel sets the current level of logging.
func SetLevel(level string) error {
	l, err := toLevel(level)
	if err != nil {
		return err
	}
	currentLevel = l
	return nil
}

// At returns whether the level will be logged currently.
func At(level string) bool {
	l, err := toLevel(level)
	if err != nil {
		return false
	}
	return currentLevel <= l
}

// Printf writes a formated message to the log.
func Printf(format string, v ...interface{}) {
	Info.Printf(format, v...)
}

// Print writes a message to the log.
func Print(v ...interface{}) {
	Info.Print(v...)
}

// Println writes a line to the log.
func Println(v ...interface{}) {
	Info.Println(v...)
}

// Fatal writes a message to the log and aborts.
func Fatal(v ...interface{}) {
	Info.Fatal(v...)
}

// Fatalf writes a formated message to the log and aborts.
func Fatalf(format string, v ...interface{}) {
	Info.Fatalf(format, v...)
}

// Connect connects all non-custom loggers (those not created by New) in this address space to a GCP Logging
// instance writing to a given logName.
func Connect(projectID, logName string) error {
	var err error
	client, err = newClient(projectID, logName)
	if err != nil {
		return err
	}
	return nil
}

// newClient creates a new client connected to the GCP Logging API with an assigned logName.
func newClient(projectID, logName string) (*logging.Client, error) {
	client, err := logging.NewClient(context.Background(), projectID, logName, cloud.WithScopes(logging.Scope))
	if err != nil {
		return nil, err
	}
	return client, nil
}

// newLogger instantiates an implicit Logger using the default client.
func newLogger(level level, cloudLevel logging.Level) Logger {
	return &logger{
		level:      level,
		cloudLevel: cloudLevel,
	}
}
<|endoftext|>


<commit_before>package job

import (
	"context"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/zrepl/zrepl/config"
	"github.com/zrepl/zrepl/daemon/filters"
	"github.com/zrepl/zrepl/daemon/job/wakeup"
	"github.com/zrepl/zrepl/daemon/logging"
	"github.com/zrepl/zrepl/daemon/pruner"
	"github.com/zrepl/zrepl/daemon/snapper"
	"github.com/zrepl/zrepl/endpoint"
	"github.com/zrepl/zrepl/zfs"
)

type SnapJob struct {
	name     string
	fsfilter zfs.DatasetFilter
	snapper  *snapper.PeriodicOrManual

	prunerFactory *pruner.SinglePrunerFactory

	promPruneSecs *prometheus.HistogramVec // no labels!

	pruner *pruner.Pruner
}

func (j *SnapJob) Name() string { return j.name }

func (j *SnapJob) getPruner(ctx context.Context, sender *endpoint.Sender) *pruner.Pruner {
	p := j.prunerFactory.BuildSinglePruner(ctx, sender, sender)
	return p
}

func (j *SnapJob) Type() Type { return TypeSnap }

func (j *SnapJob) RunPeriodic(ctx context.Context, wakeUpCommon chan<- struct{}) {
	j.snapper.Run(ctx, wakeUpCommon)
}

func (j *SnapJob) FSFilter() zfs.DatasetFilter {
	return j.fsfilter
}

func snapJob(g *config.Global, in *config.SnapJob) (j *SnapJob, err error) {
	j = &SnapJob{}
	fsf, err := filters.DatasetMapFilterFromConfig(in.Filesystems)
	if err != nil {
		return nil, errors.Wrap(err, "cannnot build filesystem filter")
	}
	j.fsfilter = fsf

	if j.snapper, err = snapper.FromConfig(g, fsf, in.Snapshotting); err != nil {
		return nil, errors.Wrap(err, "cannot build snapper")
	}
	j.name = in.Name
	j.promPruneSecs = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace:   "zrepl",
		Subsystem:   "pruning",
		Name:        "time",
		Help:        "seconds spent in pruner",
		ConstLabels: prometheus.Labels{"zrepl_job": j.name},
	}, []string{"prune_side"})
	j.prunerFactory, err = pruner.NewSinglePrunerFactory(in.Pruning, j.promPruneSecs)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build snapjob pruning rules")
	}
	return j, nil
}

func (j *SnapJob) RegisterMetrics(registerer prometheus.Registerer) {
	registerer.MustRegister(j.promPruneSecs)
}

type SnapJobStatus struct {
	Pruning *pruner.Report
}

func (j *SnapJob) Status() *Status {
	s := &SnapJobStatus{}
	t := j.Type()
	if j.pruner != nil {
		s.Pruning = j.pruner.Report()
	}
	return &Status{Type: t, JobSpecific: s}
}

func (j *SnapJob) Run(ctx context.Context) {
	log := GetLogger(ctx)
	ctx = logging.WithSubsystemLoggers(ctx, log)

	defer log.Info("job exiting")

	periodicDone := make(chan struct{})
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	go j.RunPeriodic(ctx, periodicDone)

	invocationCount := 0
outer:
	for {
		log.Info("wait for wakeups")
		select {
		case <-ctx.Done():
			log.WithError(ctx.Err()).Info("context")
			break outer

		case <-wakeup.Wait(ctx):
		case <-periodicDone:
		}
		invocationCount++
		invLog := log.WithField("invocation", invocationCount)
		j.doPrune(WithLogger(ctx, invLog))
	}
}

func (j *SnapJob) doPrune(ctx context.Context) {
	log := GetLogger(ctx)
	ctx = logging.WithSubsystemLoggers(ctx, log)
	sender := endpoint.NewSender(j.FSFilter())
	j.pruner = j.getPruner(ctx, sender)
	log.Info("start pruning")
	j.pruner.Prune()
	log.Info("finished pruning")
}
<commit_msg>Inlined a couple legacy leftover functions from the mode copypasta<commit_after>package job

import (
	"context"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/zrepl/zrepl/config"
	"github.com/zrepl/zrepl/daemon/filters"
	"github.com/zrepl/zrepl/daemon/job/wakeup"
	"github.com/zrepl/zrepl/daemon/logging"
	"github.com/zrepl/zrepl/daemon/pruner"
	"github.com/zrepl/zrepl/daemon/snapper"
	"github.com/zrepl/zrepl/endpoint"
	"github.com/zrepl/zrepl/zfs"
)

type SnapJob struct {
	name     string
	fsfilter zfs.DatasetFilter
	snapper  *snapper.PeriodicOrManual

	prunerFactory *pruner.SinglePrunerFactory

	promPruneSecs *prometheus.HistogramVec // no labels!

	pruner *pruner.Pruner
}

func (j *SnapJob) Name() string { return j.name }

func (j *SnapJob) Type() Type { return TypeSnap }

func snapJob(g *config.Global, in *config.SnapJob) (j *SnapJob, err error) {
	j = &SnapJob{}
	fsf, err := filters.DatasetMapFilterFromConfig(in.Filesystems)
	if err != nil {
		return nil, errors.Wrap(err, "cannnot build filesystem filter")
	}
	j.fsfilter = fsf

	if j.snapper, err = snapper.FromConfig(g, fsf, in.Snapshotting); err != nil {
		return nil, errors.Wrap(err, "cannot build snapper")
	}
	j.name = in.Name
	j.promPruneSecs = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace:   "zrepl",
		Subsystem:   "pruning",
		Name:        "time",
		Help:        "seconds spent in pruner",
		ConstLabels: prometheus.Labels{"zrepl_job": j.name},
	}, []string{"prune_side"})
	j.prunerFactory, err = pruner.NewSinglePrunerFactory(in.Pruning, j.promPruneSecs)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build snapjob pruning rules")
	}
	return j, nil
}

func (j *SnapJob) RegisterMetrics(registerer prometheus.Registerer) {
	registerer.MustRegister(j.promPruneSecs)
}

type SnapJobStatus struct {
	Pruning *pruner.Report
}

func (j *SnapJob) Status() *Status {
	s := &SnapJobStatus{}
	t := j.Type()
	if j.pruner != nil {
		s.Pruning = j.pruner.Report()
	}
	return &Status{Type: t, JobSpecific: s}
}

func (j *SnapJob) Run(ctx context.Context) {
	log := GetLogger(ctx)
	ctx = logging.WithSubsystemLoggers(ctx, log)

	defer log.Info("job exiting")

	periodicDone := make(chan struct{})
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	go 	j.snapper.Run(ctx, periodicDone)

	invocationCount := 0
outer:
	for {
		log.Info("wait for wakeups")
		select {
		case <-ctx.Done():
			log.WithError(ctx.Err()).Info("context")
			break outer

		case <-wakeup.Wait(ctx):
		case <-periodicDone:
		}
		invocationCount++
		invLog := log.WithField("invocation", invocationCount)
		j.doPrune(WithLogger(ctx, invLog))
	}
}

func (j *SnapJob) doPrune(ctx context.Context) {
	log := GetLogger(ctx)
	ctx = logging.WithSubsystemLoggers(ctx, log)
	sender := endpoint.NewSender(j.fsfilter)
	j.pruner = 	j.prunerFactory.BuildSinglePruner(ctx, sender, sender)
	log.Info("start pruning")
	j.pruner.Prune()
	log.Info("finished pruning")
}
<|endoftext|>


<commit_before>package dnsr

import (
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/miekg/dns"
)

var (
	DebugLogger io.Writer
)

func logMaxRecursion(qname string, qtype string, depth int) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s Error: MAX RECURSION @ %s %s %d\n",
		strings.Repeat("│   ", depth-1), qname, qtype, depth)
}

func logResolveStart(qname string, qtype string, depth int) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s┌─── resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth-1), qname, qtype, depth)
}

func logResolveEnd(qname string, qtype string, depth int, start time.Time) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s└─── %dms: resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth-1), dur/time.Millisecond, qname, qtype, depth)
}

func logCNAME(depth int, cname string) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s│    CNAME: %s\n", strings.Repeat("│   ", depth-1), cname)
}

func logExchange(host string, qmsg *dns.Msg, depth int, start time.Time, err error) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s│    %dms: dig +norecurse @%s %s %s\n",
		strings.Repeat("│   ", depth-1), dur/time.Millisecond, host, qmsg.Question[0].Name, dns.TypeToString[qmsg.Question[0].Qtype])
	if err != nil {
		fmt.Fprintf(DebugLogger, "%s│    %dms: ERROR: %s\n",
			strings.Repeat("│   ", depth-1), dur/time.Millisecond, err.Error())
	}
}
<commit_msg>round those corners<commit_after>package dnsr

import (
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/miekg/dns"
)

var (
	DebugLogger io.Writer
)

func logMaxRecursion(qname string, qtype string, depth int) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s Error: MAX RECURSION @ %s %s %d\n",
		strings.Repeat("│   ", depth-1), qname, qtype, depth)
}

func logResolveStart(qname string, qtype string, depth int) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s╭─── resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth-1), qname, qtype, depth)
}

func logResolveEnd(qname string, qtype string, depth int, start time.Time) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s╰─── %dms: resolve(\"%s\", \"%s\", %d)\n",
		strings.Repeat("│   ", depth-1), dur/time.Millisecond, qname, qtype, depth)
}

func logCNAME(depth int, cname string) {
	if DebugLogger == nil {
		return
	}
	fmt.Fprintf(DebugLogger, "%s│    CNAME: %s\n", strings.Repeat("│   ", depth-1), cname)
}

func logExchange(host string, qmsg *dns.Msg, depth int, start time.Time, err error) {
	if DebugLogger == nil {
		return
	}
	dur := time.Since(start)
	fmt.Fprintf(DebugLogger, "%s│    %dms: dig +norecurse @%s %s %s\n",
		strings.Repeat("│   ", depth-1), dur/time.Millisecond, host, qmsg.Question[0].Name, dns.TypeToString[qmsg.Question[0].Qtype])
	if err != nil {
		fmt.Fprintf(DebugLogger, "%s│    %dms: ERROR: %s\n",
			strings.Repeat("│   ", depth-1), dur/time.Millisecond, err.Error())
	}
}
<|endoftext|>


<commit_before>//	logplus.Logln("this is example")

package logplus

import (
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

const (
	resetAll = "\033[0m"
)

var (
	timeFormat = time.RFC3339
	logLevel   = INFO
)

type Color int

const (
	ForegroundDefault Color = iota
	ForegroundBlack
	ForegroundRed
	ForegroundGreen
	ForegroundYellow
	ForegroundBlue
	ForegroundMagenta
	ForegroundCyan
	ForegroundWhite

	BackgroundDefault
	BackgroundBlack
	BackgroundRed
	BackgroundGreen
	BackgroundYellow
	BackgroundBlue
	BackgroundMagenta
	BackgroundCyan
	BackgroundWhite
)

func (color Color) String() string {
	switch color {
	case ForegroundDefault:
		return "\033[39m"
	case ForegroundBlack:
		return "\033[30m"
	case ForegroundRed:
		return "\033[31m"
	case ForegroundGreen:
		return "\033[32m"
	case ForegroundYellow:
		return "\033[33m"
	case ForegroundBlue:
		return "\033[34m"
	case ForegroundMagenta:
		return "\033[35m"
	case ForegroundCyan:
		return "\033[36m"
	case ForegroundWhite:
		return "\033[97m"
	case BackgroundDefault:
		return "\033[49m"
	case BackgroundBlack:
		return "\033[40m"
	case BackgroundRed:
		return "\033[41m"
	case BackgroundGreen:
		return "\033[42m"
	case BackgroundYellow:
		return "\033[43m"
	case BackgroundBlue:
		return "\033[44m"
	case BackgroundMagenta:
		return "\033[45m"
	case BackgroundCyan:
		return "\033[46m"
	case BackgroundWhite:
		return "\033[107m"
	}
	panic("Unknown value")
}

type LogLevel int

const (
	FATAL LogLevel = iota
	ERROR
	WARN
	INFO
	DEBUG
)

func (level LogLevel) String() string {
	switch level {
	case FATAL:
		return "FATAL"
	case ERROR:
		return "ERROR"
	case WARN:
		return "WARN"
	case INFO:
		return "INFO"
	case DEBUG:
		return "DEBUG"
	}
	panic("Unknown value")
}

type CallInfo struct {
	PackageName string
	FileName    string
	FuncName    string
	Line        int
}

func (ci CallInfo) String() string {
	return fmt.Sprintf("%s#%s (%s:%d)", ci.PackageName, ci.FuncName, ci.FileName, ci.Line)
}

func SetTimeFormat(tf string) {
	timeFormat = tf
}

func SetLevel(level LogLevel) {
	logLevel = level
}

func isLogAvailable(level LogLevel) bool {
	if level <= logLevel {
		return true
	}
	return false
}

func Log(log ...interface{}) {
	info := getCallInfo()
	fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(log...)
}

func Logf(format string, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Printf(format, log...)
}

func Logln(log ...interface{}) {
	info := getCallInfo()
	fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Println(log...)
}

func Colored(color Color, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf("%s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(color)
	fmt.Print(log...)
	fmt.Print(resetAll)
}

func Coloredf(color Color, format string, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf("%s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(color)
	fmt.Printf(format, log...)
	fmt.Print(resetAll)
}

func Coloredln(color Color, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf("%s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(color)
	fmt.Print(log...)
	fmt.Println(resetAll)
}

func Fatal(log ...interface{}) {
	if isLogAvailable(FATAL) {
		fmt.Print(concat("", BackgroundRed.String(), FATAL.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Print(log...)
	}
}

func Fatalf(format string, log ...interface{}) {
	if isLogAvailable(FATAL) {
		fmt.Print(concat("", BackgroundRed.String(), FATAL.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Printf(format, log...)
	}
}

func Fatalln(log ...interface{}) {
	if isLogAvailable(FATAL) {
		fmt.Print(concat("", BackgroundRed.String(), FATAL.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Println(log...)
	}
}

func Error(log ...interface{}) {
	if isLogAvailable(ERROR) {
		fmt.Print(concat("", BackgroundMagenta.String(), ERROR.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Print(log...)
	}
}

func Errorf(format string, log ...interface{}) {
	if isLogAvailable(ERROR) {
		fmt.Print(concat("", BackgroundMagenta.String(), ERROR.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Printf(format, log...)
	}
}

func Errorln(log ...interface{}) {
	if isLogAvailable(ERROR) {
		fmt.Print(concat("", BackgroundMagenta.String(), ERROR.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Println(log...)
	}
}

func Warn(log ...interface{}) {
	if isLogAvailable(WARN) {
		fmt.Print(concat("", BackgroundYellow.String(), WARN.String(), resetAll, " "))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Print(log...)
	}
}

func Warnf(format string, log ...interface{}) {
	if isLogAvailable(WARN) {
		fmt.Print(concat("", BackgroundYellow.String(), WARN.String(), resetAll, " "))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Printf(format, log...)
	}
}

func Warnln(log ...interface{}) {
	if isLogAvailable(WARN) {
		fmt.Print(concat("", BackgroundYellow.String(), WARN.String(), resetAll, " "))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Println(log...)
	}
}

func Info(log ...interface{}) {
	if isLogAvailable(INFO) {
		fmt.Print(concat("", BackgroundGreen.String(), INFO.String(), resetAll, " "))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Print(log...)
	}
}

func Infof(format string, log ...interface{}) {
	if isLogAvailable(INFO) {
		fmt.Print(concat("", BackgroundGreen.String(), INFO.String(), resetAll, " "))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Printf(format, log...)
	}
}

func Infoln(log ...interface{}) {
	if isLogAvailable(INFO) {
		fmt.Print(concat("", BackgroundGreen.String(), INFO.String(), resetAll, " "))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Println(log...)
	}
}

func Debug(log ...interface{}) {
	if isLogAvailable(DEBUG) {
		fmt.Print(concat("", BackgroundCyan.String(), DEBUG.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Print(log...)
	}
}

func Debugf(format string, log ...interface{}) {
	if isLogAvailable(DEBUG) {
		fmt.Print(concat("", BackgroundCyan.String(), DEBUG.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Printf(format, log...)
	}
}

func Debugln(log ...interface{}) {
	if isLogAvailable(DEBUG) {
		fmt.Print(concat("", BackgroundCyan.String(), DEBUG.String(), resetAll))
		info := getCallInfo()
		fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
		fmt.Println(log...)
	}
}

func concat(sep string, strs ...string) string {
	var result = make([]byte, 0, 100)
	for i, _ := range strs {
		result = append(result, strs[i]...)
		if i < len(strs)-1 {
			result = append(result, sep...)
		}
	}
	return string(result)
}

func getCallInfo() *CallInfo {
	pc, filePath, line, _ := runtime.Caller(2)
	_, fileName := path.Split(filePath)
	parts := strings.Split(runtime.FuncForPC(pc).Name(), ".")
	pl := len(parts)
	packageName := ""
	funcName := parts[pl-1]
	if parts[pl-2][0] == '(' {
		funcName = parts[pl-2] + "." + funcName
		packageName = strings.Join(parts[0:pl-2], ".")
	} else {
		packageName = strings.Join(parts[0:pl-1], ".")
	}

	return &CallInfo{
		PackageName: packageName,
		FileName:    fileName,
		FuncName:    funcName,
		Line:        line,
	}
}
<commit_msg>Added new log level FATAL<commit_after>//	logplus.Logln("this is example")

package logplus

import (
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

const (
	resetAll = "\033[0m"
)

var (
	timeFormat = time.RFC3339
	logLevel   = INFO
)

type Color int

const (
	ForegroundDefault Color = iota
	ForegroundBlack
	ForegroundRed
	ForegroundGreen
	ForegroundYellow
	ForegroundBlue
	ForegroundMagenta
	ForegroundCyan
	ForegroundWhite

	BackgroundDefault
	BackgroundBlack
	BackgroundRed
	BackgroundGreen
	BackgroundYellow
	BackgroundBlue
	BackgroundMagenta
	BackgroundCyan
	BackgroundWhite
)

func (color Color) String() string {
	switch color {
	case ForegroundDefault:
		return "\033[39m"
	case ForegroundBlack:
		return "\033[30m"
	case ForegroundRed:
		return "\033[31m"
	case ForegroundGreen:
		return "\033[32m"
	case ForegroundYellow:
		return "\033[33m"
	case ForegroundBlue:
		return "\033[34m"
	case ForegroundMagenta:
		return "\033[35m"
	case ForegroundCyan:
		return "\033[36m"
	case ForegroundWhite:
		return "\033[97m"
	case BackgroundDefault:
		return "\033[49m"
	case BackgroundBlack:
		return "\033[40m"
	case BackgroundRed:
		return "\033[41m"
	case BackgroundGreen:
		return "\033[42m"
	case BackgroundYellow:
		return "\033[43m"
	case BackgroundBlue:
		return "\033[44m"
	case BackgroundMagenta:
		return "\033[45m"
	case BackgroundCyan:
		return "\033[46m"
	case BackgroundWhite:
		return "\033[107m"
	}
	panic("Unknown value")
}

type LogLevel int

const (
	PANIC LogLevel = iota
	FATAL
	ERROR
	WARN
	INFO
	DEBUG
)

func (level LogLevel) String() string {
	switch level {
	case PANIC:
		return "PANIC"
	case FATAL:
		return "FATAL"
	case ERROR:
		return "ERROR"
	case WARN:
		return "WARN"
	case INFO:
		return "INFO"
	case DEBUG:
		return "DEBUG"
	}
	panic("Unknown value")
}

type CallInfo struct {
	PackageName string
	FileName    string
	FuncName    string
	Line        int
}

func (ci CallInfo) String() string {
	return fmt.Sprintf("%s#%s (%s:%d)", ci.PackageName, ci.FuncName, ci.FileName, ci.Line)
}

func SetTimeFormat(tf string) {
	timeFormat = tf
}

func SetLevel(level LogLevel) {
	logLevel = level
}

func isLogAvailable(level LogLevel) bool {
	if level <= logLevel {
		return true
	}
	return false
}

func Log(log ...interface{}) {
	info := getCallInfo()
	fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(log...)
}

func Logf(format string, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Printf(format, log...)
}

func Logln(log ...interface{}) {
	info := getCallInfo()
	fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Println(log...)
}

func Colored(color Color, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf("%s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(color)
	fmt.Print(log...)
	fmt.Print(resetAll)
}

func Coloredf(color Color, format string, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf("%s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(color)
	fmt.Printf(format, log...)
	fmt.Print(resetAll)
}

func Coloredln(color Color, log ...interface{}) {
	info := getCallInfo()
	fmt.Printf("%s [%s] <%d> ", time.Now().Format(timeFormat), info, os.Getpid)
	fmt.Print(color)
	fmt.Print(log...)
	fmt.Println(resetAll)
}

func Panic(log ...interface{}) {
	if isLogAvailable(PANIC) {
		printLogInfo(BackgroundRed, PANIC)
		fmt.Print(log...)

		panic(fmt.Sprint(log...))
	}
}

func Panicf(format string, log ...interface{}) {
	if isLogAvailable(PANIC) {
		printLogInfo(BackgroundRed, PANIC)
		fmt.Printf(format, log...)

		panic(fmt.Sprintf(format, log...))
	}
}

func Panicln(log ...interface{}) {
	if isLogAvailable(PANIC) {
		printLogInfo(BackgroundRed, PANIC)
		fmt.Println(log...)

		panic(fmt.Sprintln(log...))
	}
}

func Fatal(log ...interface{}) {
	if isLogAvailable(FATAL) {
		printLogInfo(BackgroundRed, FATAL)
		fmt.Print(log...)

		os.Exit(1)
	}
}

func Fatalf(format string, log ...interface{}) {
	if isLogAvailable(FATAL) {
		printLogInfo(BackgroundRed, FATAL)
		fmt.Printf(format, log...)

		os.Exit(1)
	}
}

func Fatalln(log ...interface{}) {
	if isLogAvailable(FATAL) {
		printLogInfo(BackgroundRed, FATAL)
		fmt.Println(log...)

		os.Exit(1)
	}
}

func Error(log ...interface{}) {
	if isLogAvailable(ERROR) {
		printLogInfo(BackgroundMagenta, ERROR)
		fmt.Print(log...)
	}
}

func Errorf(format string, log ...interface{}) {
	if isLogAvailable(ERROR) {
		printLogInfo(BackgroundMagenta, ERROR)
		fmt.Printf(format, log...)
	}
}

func Errorln(log ...interface{}) {
	if isLogAvailable(ERROR) {
		printLogInfo(BackgroundMagenta, ERROR)
		fmt.Println(log...)
	}
}

func Warn(log ...interface{}) {
	if isLogAvailable(WARN) {
		printLogInfo(BackgroundYellow, WARN)
		fmt.Print(log...)
	}
}

func Warnf(format string, log ...interface{}) {
	if isLogAvailable(WARN) {
		printLogInfo(BackgroundYellow, WARN)
		fmt.Printf(format, log...)
	}
}

func Warnln(log ...interface{}) {
	if isLogAvailable(WARN) {
		printLogInfo(BackgroundYellow, WARN)
		fmt.Println(log...)
	}
}

func Info(log ...interface{}) {
	if isLogAvailable(INFO) {
		printLogInfo(BackgroundGreen, INFO)
		fmt.Print(log...)
	}
}

func Infof(format string, log ...interface{}) {
	if isLogAvailable(INFO) {
		printLogInfo(BackgroundGreen, INFO)
		fmt.Printf(format, log...)
	}
}

func Infoln(log ...interface{}) {
	if isLogAvailable(INFO) {
		printLogInfo(BackgroundGreen, INFO)
		fmt.Println(log...)
	}
}

func Debug(log ...interface{}) {
	if isLogAvailable(DEBUG) {
		printLogInfo(BackgroundCyan, DEBUG)
		fmt.Print(log...)
	}
}

func Debugf(format string, log ...interface{}) {
	if isLogAvailable(DEBUG) {
		printLogInfo(BackgroundCyan, DEBUG)
		fmt.Printf(format, log...)
	}
}

func Debugln(log ...interface{}) {
	if isLogAvailable(DEBUG) {
		printLogInfo(BackgroundCyan, DEBUG)
		fmt.Println(log...)
	}
}

func concat(sep string, strs ...string) string {
	var result = make([]byte, 0, 100)
	for i, _ := range strs {
		result = append(result, strs[i]...)
		if i < len(strs)-1 {
			result = append(result, sep...)
		}
	}
	return string(result)
}

func printLogInfo(color Color, level LogLevel) {
	fmt.Print(concat("", color.String(), level.String(), resetAll))
	fmt.Printf(" %s [%s] <%d> ", time.Now().Format(timeFormat), getCallInfo(), os.Getpid)
}

func getCallInfo() *CallInfo {
	pc, filePath, line, _ := runtime.Caller(2)
	_, fileName := path.Split(filePath)
	parts := strings.Split(runtime.FuncForPC(pc).Name(), ".")
	pl := len(parts)
	packageName := ""
	funcName := parts[pl-1]
	if parts[pl-2][0] == '(' {
		funcName = parts[pl-2] + "." + funcName
		packageName = strings.Join(parts[0:pl-2], ".")
	} else {
		packageName = strings.Join(parts[0:pl-1], ".")
	}

	return &CallInfo{
		PackageName: packageName,
		FileName:    fileName,
		FuncName:    funcName,
		Line:        line,
	}
}
<|endoftext|>