Zend_Db and UTF-8

December 14th, 2008

Today I discovered a problem with my PHP/MySQL application. SQL data coming from the Zend_Db logic has the wrong encoding.

Like IMO all modern applications should, my application only uses UTF-8 for displaying and handling all non-ASCII characters. To make the MySQL server understand that I talk UTF-8 to it, I always use the following immediately after connecting:
Read the rest of this entry »

Acer Aspire One

November 23rd, 2008

Three Laptops

For a few years I have been working with a Toshiba Satellite Pro 4600. A nice one, but heavy and huge and the machine started to get ramshackle and having more and more serious problems, like a bad battery, unexpected fallouts and display backlight problems. Therefore I decided to buy a new laptop so I can still work in the train.
My attention felt on the new series of netbooks. Nice small laptops of only about one kilogram, I could easily take with me when traveling, instead of the almost 3kg Toshiba brick I was used to.
Read the rest of this entry »

Trainingsrondje 70km

November 23rd, 2008

Dit weekend eens weer wat tijd uitgetrokken om een wat langer trainingsrondje te rijden. Of misschien liever: een “voorkoming van teveel aftakeling”-rondje, want afgelopen tijd heb ik te weinig gefietst, wat ook te merken was.

Route

De route die ik nu gereden heb is lekker afwisselend, bos, hellingen en een stuk lekdijk met flinke tegenwind en zwaar lopend grof asfalt. Het gaat vanaf de Berenkuil in Utrecht naar De Bilt, via de N237 langs Soesterberg (vervelende stoplichten), dan het Zeisterspoor (lekkere fietspad- en stoplichtloze weg met hoogteverschillen) naar Leusden. Vanaf daar de N227 naar het zuiden, via Doorn, Langbroek en Cothen naar Wijk bij Duurstede, onderweg aardige hoogteverschillen waar je ff flink tegenaan kan beuken om het hartslag op maximaal te krijgen. Bij Cothen kwam ik een hagel/sneeuwbui tegen en lag er overal natte sneeuw op de weg, zwaar en gelibberig, terwijl de zon alweer fel begon te schijnen. Vervolgens de Lekdijk. Erg grof en zwaar asfalt, niet relaxed fietsen, maar samen met de tegenwind en een brommer voor me uit die net 45km/h gaat een aansproring om nog ff flink door te stampen. Dus zoals het hoort goed moe bij Niewegein het laatste stuk naar huis.

Links: GPX bestand KML bestand Google Maps

Caching MySQL queries with memcache in PHP

November 17th, 2008

Recently I had to improve performance of several PHP sites. One of the problems is complicated queries, that take a long time to compute by the MySQL server. Therefore, I decided to look for a solution.

I found a nice solution on http://pureform.wordpress.com/2008/05/21/using-memcache-with-mysql-and-php/ and I decided to work this out for my sites.

For php4 sites I rewrote this code to a simple class:


/**
* Bastiaan Welmers - 20081013
* instataniate this class and use mysql_query_cache instead of mysql_query for slow queries
*
* php4 style class
*
*/

class memCacheDb
{
	var $memcache;

	// constructor
	function memCacheDb()
	{
		# Connect to memcache:
		$this->memcache = new Memcache;
		$this->memcache->connect('localhost', 11211) or die ("Could not connect to memcache");
	}

	# Gets key / value pair into memcache ... called by mysql_query_cache()
	function getCache($key) {
		return ($this->memcache) ? $this->memcache->get($key) : false;
	}

	# Puts key / value pair into memcache ... called by mysql_query_cache()
	function setCache($key,$object,$timeout = 60) {
		return ($this->memcache) ? $this->memcache->set($key,$object,MEMCACHE_COMPRESSED,$timeout) : false;
	}

	# Caching version of mysql_query()
	function mysql_query_cache($sql, $linkIdentifier = false, $timeout = 600 /* = 10 minutes expiration */) {
		if (!($cache = $this->getCache(md5("mysql_query" . $sql)))) {
			$cache = false;
			$r = ($linkIdentifier !== false) ? mysql_query($sql,$linkIdentifier) : mysql_query($sql);
			if (is_resource($r) && (($rows = mysql_num_rows($r)) != 0)) {
				for ($i=0;$i<$rows;$i++) {
					$fields = mysql_num_fields($r);
					$row = mysql_fetch_array($r);
					for ($j=0;$j<$fields;$j++) {
						if ($i == 0) {
							$columns[$j] = mysql_field_name($r,$j);
						}
						$cache[$i][$columns[$j]] = $row[$j];
					}
				}
				if (!$this->setCache(md5("mysql_query" . $sql),$cache,$timeout)) {
					die('Error trying to connect to memcache');
					# If we get here, there isn't a memcache daemon running or responding
				}
			}
		}
		return $cache;
	}
}

For the PHP5 applications I wrote a PHP5 style style class and I added expiration logic. You can now use MemCacheDb::deleteMatching($arrayOfKeywords) to remove queries from cache that contain certain keywords. You can use this for example in your CMS, just call the deleteMatching method in the update/creation logic with the updated table name in the keywords and all cached queries will expire.


/**
* Bastiaan Welmers - 20081013
* instataniate this class and use this mysql_query methoid instead of mysql_query for slow queries
* it wil return a array with assosiative arrays with the output.
* So use foreach($mdb->mysql_query($query) as $rows) {}
*
* php5 style class
*
*/

class MemCacheDb
{

	private $_memcache;
	private $_appKey = 'Change this key for your application';
	private $_queries = null;

	// constructor
	public function __construct()
	{
		// Connect to memcache:
		$this->_memcache = new Memcache();
		$this->_memcache->connect('localhost', 11211) or die ("Could not connect to memcache");
		$this->_appKey = md5(__FILE__ . $this->_appKey);
	}

	public function __destruct()
	{
		if (is_array($this->_queries))
		{
			$this->_saveQueries();
		}
	}

	public function getStats()
	{
		return $this->_memcache->getStats();
	}

	private function _loadQueries()
	{
		if (!is_array($this->_queries))
		{
			$queries = $this->_memcache->get($this->_appKey . '__queries');
			if ($queries !== false && is_array($queries))
				$this->_queries = $queries;
			else
				$this->_queries = array();
		}
	}

	public function getQueries()
	{
		$this->_loadQueries();
		return $this->_queries;
	}

	public function searchQueries($search)
	{
		$queries = array();
		foreach($this->getQueries() as $sql => $timestamp)
		{
			if (strpos($sql, $search) !== false)
			{
				$queries[] = $sql;
			}
		}
		return $queries;
	}

	public function deleteQuery($sql)
	{
		$this->_memcache->delete($this->_getQueryKey($sql));
		$this->_loadQueries();
		if (isset($this->_queries[$sql]))
			unset($this->_queries[$sql]);
		$this->_saveQueries();
	}

	public function deleteMatching($searchItems)
	{
		if (!is_array($searchItems))
		{
			$searchItems = array($searchItems);
		}

		foreach($searchItems as $searchItem)
		{
			foreach($this->searchQueries($searchItem) as $query)
				$this->deleteQuery($query);
		}
	}

	public function deleteAll()
	{
		foreach($this->getQueries() as $sql => $timestamp)
		{
			$this->deleteQuery($sql);
		}
	}

	private function _saveQueries($timeout = 1800)
	{
		if (is_array($this->_queries))
		{
			// clean old stuff
			foreach($this->_queries as $sql => $timestamp)
			{
				if (time() - $timestamp > $timeout)
					unset($this->_queries[$sql]);
			}

			return $this->_memcache->set($this->_appKey . '__queries', $this->_queries, 0, $timeout);
		}
		else
			return false;
	}

	// Gets key / value pair into memcache … called by mysql_query_cache()
	private function _getCache($key) {
		return ($this->_memcache) ? $this->_memcache->get($key) : false;
	}

	// Puts key / value pair into memcache … called by mysql_query_cache()
	private function _setCache($key, $object, $timeout) {
		$this->_loadQueries();

		if ($this->_memcache instanceof Memcache)
			return $this->_memcache->set($key, $object, 0, $timeout);
		else
			return false;
	}

	private function _cacheQuery($sql, $object, $timeout)
	{
		if ($this->_setCache($this->_getQueryKey($sql), $object, $timeout))
		{
			$this->_loadQueries();
			$this->_queries[$sql] = time();
			$this->_saveQueries($timeout);
			return true;
		}
		else
		{
			return false;
		}
	}

	public function expireQuery($query)
	{
		$this->_memcache->delete($this->_getQueryKey($query));
	}

	public function mysql_query($sql, $linkIdentifier = false, $timeout = 600)
	{
		return $this->mysql_query_cache($sql, $linkIdentifier, $timeout);
	}

	private function _getQueryKey($sql)
	{
		return md5($this->_appKey . $sql);
	}

	// Caching version of mysql_query()
	public function mysql_query_cache($sql, $linkIdentifier = false, $timeout = 600 /* = 10 minutes expiration */) {
		if (!($cache = $this->_getCache($this->_getQueryKey($sql)))) {
			$cache = array();
			$r = ($linkIdentifier !== false) ? mysql_query($sql,$linkIdentifier) : mysql_query($sql);
			if (is_resource($r) && (($rows = mysql_num_rows($r)) != 0)) {
				for ($i=0;$i<$rows;$i++) {
					$fields = mysql_num_fields($r);
					$row = mysql_fetch_array($r);
					for ($j=0;$j<$fields;$j++) {
						if ($i == 0) {
							$columns[$j] = mysql_field_name($r,$j);
						}
						$cache[$i][$columns[$j]] = $row[$j];
					}
				}
				if (!$this->_cacheQuery($sql, $cache, $timeout)) {
					die('Error trying to connect to memcache');
					// If we get here, there isn't a memcache daemon running or responding
				}
			}
		}
		return $cache;
	}
}

TODO: this code need to get more documentation, and a static method to store the object instance so it can be called everywhere in the application.

Nieuwe site online

November 9th, 2008

Eindelijk, na vele jaren, is de nieuwe site online!
Even een weekendje hard doorwerken, en dan komt het er toch eindelijk van.

De nieuwe website is geschreven in Zend Framework. Dit is misschien wat overdone voor een simpele website, maar als je eenmaal in een dergelijk stramien gewend bent te programmeren, dan wil je eigenlijk niet anders. Bovendien kan ik nu mooi de Zend_Feed logic gebruiken voor de Subcontent balk aan de rechterkant. Dit wordt overigens netjes gecachet natuurlijk.

Afijn, de site is nog niet af. Er komt nog het nodige content in, is het plan. En o ja het oude reliek staat nu op http://old.welmers.net/.

CycleVision juni 2008

November 9th, 2008

Dit artikel heb ik al een tijd geleden geschreven, net na Cycle Vision, die plaatsvond in juni 2008. Het artikel is niet gepubliceerd in Ligfiets& nog, dus ik zet het bij deze toch maar online.

Welke wedstrijden zal ik dit jaar rijden? Ik heb een Quest en een XLow tot mijn beschikking, dus ik kan zowel indoor op de baan als op de buitenbaan rijden. Ik heb me alleen voor de wedstrijden op de buitenbaan ingeschreven: de 1 uursrace, het 25minuten criterium en de 6 uursrace. Dat laatste werd mij van alle kanten afgeraden, mede gezien de 2 weken dat ik ziekig ben geweest.

Alles begon vrijdagmiddag. Ik fiets samen met Robert Campbel met de XLow naar het CycleVision terrein in Sloten vanuit Haarlem. ’s-Avonds met het OV terug naar Utrecht om daar te eten en haastig de grote berg aan wedstrijdspullen in de Quest te stouwen en weer terug te rijden naar het CycleVision terrein. Ik kwam rond 23:30 aan en heb mijn tent opgezet en daarna in het donker met de Xlow en de Quest nog wat rondjes op de baan gereden. De hobbels in de baan die tijdens een proefrit vorig weekend vervelend leken vielen nu toch nog redelijk mee.

De volgende ochtend ben ik al weer vroeg wakker en neem alle tijd om mijn Quest klaar te maken. Ik zet Continental Grand Prix banden eronder op 8 bar. Later start ik om 10:21 met mijn eerste wedstrijd. Meteen volle bak, en dat voor het eerst in ongeveer 2 weken doet mijn lichaam van diverse kanten protesteren. Spieren doen pijn en ik kreeg het snel vervelend warm onder de zon met dichte deksel. De veel te grote stofzuiger achtige slang die ik gisteravond als enige kon vinden en op het laatste moment heb ingezet om iets te kunnen drinken onderweg bleek smerig en niet bruikbaar, dus kon er niet gedronken worden. Halverwege vond ik het zo vervelend worden dat ik dacht: zal ik stoppen? Daarna dacht ik rationeel na en wist dat het resultaat altijd meevalt. Ik heb uitgereden, en bleek achteraf 13e geworden te zijn met 47,2km/h. Hoewel dat nog meevalt, blijk maar weer dat de eerste wedstrijd altijd even flink wennen is, en ik neem me voor om zeker niet de 6 uurs te gaan rijden.

Die middag besluit ik lekker rustig aan te doen. Ik rijd de stad in om boodschappen te doen en vind een dierenzaak om een aquariumslang te kopen als vervanging van de stofzuigerslang om fatsoenlijk door te kunnen drinken. Ik wandel wat op de beurs rond en heb naar de wedstrijden op de binnenbaan gekeken. Ik zie de nog te starten groep voor het criterium veel te langzaam bovenin de baan rijden en ja hoor, de bovenste glijdt eruit en de hele groep tuimelt naar beneden en ligt onderaan over elkaar heen. Misschien is gelijk volle bak starten toch nuttiger?

Ik haal nog wat slaap in. Eind van de middag voel ik me weer vrij goed en eet een flinke pastamaaltijd om rond 18:00 aan de start te staan van het 25 minuten criterium. Ik heb de nieuwe hoofdkoepel van Wim Schermer te leen gekregen en dat is erg fijn. Je ziet je kilometerteller/hartslag/tijd teminste fatsoenlijk, je hebt ruimte om spullen te pakken en te eten en te drinken. Het open te klappen vizier kan op een kier gezet worden voor voldoende koeling. Verder heb je geen last van wind in je ogen en windherrie bij je oren. Dat rijdt een stuk prettiger en dus sneller. En tenslotte is het aerodynamischer en dus ook daarom sneller.

Ik ga gelijk hard van start en weet de eerste anderhalve ronde tussen Ymte, Erik en Hans te blijven. Daarna stijgt mijn hartslag boven de 180 en moet ik toch wat dimmen. Ik weet het tempo er toch aardig in te houden. De bochten zijn spannend en ik rijd er met 2 wielen, net op het randje van de weg doorheen. Ik zie de mooie nieuwe opvallende Quest van Allert in mijn spiegel bij me in de buurt blijven en doe mijn best om de afstand groot genoeg te houden. Na een paar rondes loopt er een hond langs de baan en ik rijd er rakelings langs. In mijn spiegel zie de hond in paniek de baan op rennen en Allert moest de berm in duiken om het beest te ontwijken. Hoewel ik hem nu echt kwijt ben is dit geen fijne manier. De laatste ronde zie ik Eduard Botter een poging doen mij voorbij te streven maar ik knal nog even flink door en weet overall 4e te worden. Ymte en Erik doen niet mee in het Velomobiel klassement en wordt zodoende 2e, goed voor een set van 3 mooie nieuwe HPV banden. Hartelijk dank!

Die avond geniet ik van een 2e pastamaaltijd en kletsen we wat bij op de camping onder genot van een biertje. Hans komt vragen of hij de koepel van Wim mag lenen. Ik neem me voor om morgen niet de 6 uurs te rijden, ik heb vandaag voor mijn gevoel aardig wat gegeven en wil de komende week op mijn werk ook nog wat waard zijn, dus ik geef de koepel aan hem.

De volgende ochtend zie ik vlak voor 8:00 allemaal mensen naar de start van de 6 uurs lopen en begint het flink te kriebelen. Ik voel me goed, heb lekker ontbeten, en dus… sta ik toch aan te start. Zonder koepeltje, die heb ik uitgeleend aan Hans. Stom? Misschien. Maar Hans heeft er meer aan dan ik en ik vraag me af of 6 uur lang dicht met dat geklapper nog wel leuk is. Ik zet een luchtige helm op en gebruik gewoon de deksel. De banden zien er nog redelijk uit, maar zouden ze het 6 uur volle bak volhouden? Ik zet een set reservebanden + pomp aan de kant van de baan.

Vlak na achten begint het spektakel dan. Onder applaus zet de groep zich in beweging. Ik begin vrij snel. De eerste rondes haal ik alleen maar mensen in. Hoho, ik moet nog 6 uur, beetje dimmen. Dan begint het lange, lange rijden. Ik probeer wat mensen te vinden om samen mee te rijden, maar dat is vrij lastig, omdat ik geen zin heb om te langzaam of te hard te rijden. Ymte, Hans en Erik rijden aan kop en komen op gezette tijden voorbij. Ook Eduard Botter en Arjen van Dam rijden opvallend vaak en hard voorbij. Jammer dat Arjen later een paar rondes aan de kant stil staat met een lekke band. Later rijd ik nog wat op met Harry Lieben (hij vraagt vriendelijk of hij achter me mag rijden) en Maikel Zonneveld. Walter Hoogerbeets heeft er veel plezier in en groet me vrolijk elke keer als ik hem voorbij kom.


Na 2,5 a 3 uur begint het tempo er voor mijn gevoel steeds meer uit te raken, ik word steeds vaker van alle kanten ingehaald en ik haal in met steeds minder snelheidsverschil. Mijn hartslag daalt onder de 150. Het viaductje wordt steeds vervelender, de snelheid op het viaduct daalt onder de 30. Ik drink flink door en eet wat. Ik kom voorbij mijn dipje en even later kan ik weer wat harder rijden. Na een half uur krijg ik een volgende serieuzere dip en heb ik er ook mentaal geen zin meer in. Puf puf. Kom op Bastiaan, je rijdt zo vaak 6 uur achter elkaar, waarom zou het nu dan niet kunnen? Ik gebruik de dip om te stoppen en de bijna lege waterfles bij te vullen, en even te strekken. Ik sta nu stil aan de kant. Verder? Ja? Nee? Natuurlijk wel! Hup, instappen en rijden kreng! Ik kan het gewoon, ik rijd zo vaak lang achter elkaar. Ik vertrek en krijg er weer zin in. Ik probeer weer wat mensen te zoeken. Ik zie Bernhard Böhler en Erik Baeten op de lowracer en probeer ze bij te blijven. De snelheid stijgt weer tot rond de 47 en kom het viaduct weer vlot op. Ze lijken steeds sneller te rijden, of is dit toch mijn tempo niet? Ach nog maar 2 uur te gaan van de 6 uur, gewoon volgen. Oeps, dat is toch nog 2 uur rijden. Toch maar wat rustiger aan dan.
Even later zie ik Ymte stoppen. Om af te koelen blijkt later, na 2 rondes zie ik hem zonder hoofdkapje weer hard van start gaan.

De laatste uren kan ik vrij stabiel op de 42 a 43km/h blijven rijden. Is het leuk? Ik weet het niet. Gedachtes doemen op als: Vind ik veel en lang fietsen leuk? Zou Parijs-Brest-Parijs iets voor me zijn? Ik twijfel er nu weer hard aan. Later krijg ik veel zin in lekker eten en ruik ik ineens allemaal heerlijke geuren van vers gebakken brood, warm eten en fris bier. Ik heb het blijkbaar hard nodig, want het is er echt nog niet. Ik eet nog maar een halfgeplete warme melig muesliereep en neem een flinke slok water. Ik rijd gestaag door neem me voor om het laatste kwartier een eindsprint in te zetten en om alles eruit te persen. Helaas blijkt dat niet meer mogenlijk, ik heb alles gegeven. Ik probeer de laatste minuten de snelheid er nog zoveel mogenlijk in te houden, maar het is lastig. Het bord bij de wedstrijdkeet telt af. 25 minuten. 20 minuten 10 minuten. Dan eindelijk de laatste ronde….. En dan de finish!!

En de uitrijdronde. Ik ben trots dat ik mijn eerste 6 uurs uitgereden heb. Ik feliciteer en groet mensen, en klets wat tijdens de uitrijdronde. Ik probeer uit mijn fiets te komen. En dat blijkt ineens serieus lastig. Alles voelt stijf en stram. Mijn bilspieren bleken vrij ver heen te zijn en ik strompel voorzichtig richting camping. Het is ondertussen hard gaan waaien, mijn tent is half weggegwaaid en alle spullen in de voortent zijn uitgewaaid over de camping. Ik moet liggen, ik ga in mijn warme zonbeschenen tent liggen, ik moet liggen. Ik eet wat brood, de flessen frisdrank en bier blijken door de zon heel warm te zijn geworden, bah. Net als de tent waar ik nu in lig, dit wordt stomen. Dit is niks. Ik strompel terug naar de kantine en ga aan de bar zitten en krijg wat koel bier voorgeschoteld. Zo, dat is beter. Maar… Hoe moet dat verder nu? Ik ben echt helemaal afgepeigerd. Ik kan moeilijk lopen en zitten, dat wordt niks komende week… Ach we zullen het wel zien…

Na wat uurtjes rust in het Velodrome gaat het opeens weer een stuk beter. Mijn bilspieren doen minder zeer en ik kan weer normaal lopen. Ga ik blijven vanacht zoals ik in gedachten had? Het waait hard, het is warm, en ik ben mentaal moe en chagerijnig aan het worden, ik heb er geen zin meer in, ik wil lekker naar huis. De niet gebruikte Xlow kan ik meegeven aan Arvid, bedankt! Ik ruim mijn tent op en stouw de bende met moeite weer in mijn Quest. Dat past niet want ik heb nu wedstrijdspullen plus de spullen van mijn werk. Ik heb een bui van wat maakt het allemaal nog uit en ik gooi allemaal niet direct noodzakelijke dingen in de vuilnisbak of geef ze weg.

Eindelijk alle spullen weggewerkt. Kan ik nog fietsen? Dat gaat wonderwel weer vrij aardig. Ik fiets met Milan op mijn dooie gemak naar huis. Daarna is het spullen in de gang pleuren, eten, heel even TV en dan lekker naar bed, om de volgende dag om 7 uur met goed en niet al te duf gevoel weer naar mijn werk te gaan, dit keer maar met de trein.

Ik heb heerlijk genoten tijdens dit voor mij vrij extreme en goedbenutte weekend. Mijn hartelijke dank aan de wedstrijdleiding, tijdregistratie voor de (door mij ervaren) vlekkeloze wedstrijden en alle andere vrijwilligers die dit alles mogelijk gemaakt hebben.

WK 2008 @ Bentwaters, UK

August 18th, 2008

Een paar foto’s van het WK: http://www.welmers.net/~bastiaan/wc2008/

Een ordinaire sleur-pleur voor de mensen die niet gejoined waren op #ligfiets toen ik dit kletste:

18:54 < BlondeDonder> bastiaan: hoe ging wk?
18:57 < bastiaan> BlondeDonder: was erg leuk
18:57 < bastiaan> BlondeDonder: ging op zich wel redelijk, hoewel ik wel wat beter m’n best had kunnen/moeten doen
18:57 < BlondeDonder> nog goeie tijden gehaald?
18:57 < bastiaan> beste plaats haalde ik op de 1km
18:57 < bastiaan> en dat met een oude zware quest… ;)
18:57 < bastiaan> 4e
18:58 < bastiaan> 3 uurs / 1 uurs / criterium zat ik toch wat onder m’n doen
18:59 < bastiaan> oh ja en de 200m sprint was niet ideaal, ik was nog vol op aan het acceleren toen ik al in het 200m stuk zat
18:59 < bastiaan> +re
19:00 < bastiaan> vrijdag was niet zo ideaal, mega traag naar de camping treuzelen, en dan daarna nog ff snel naar Ipswich heen en weer
19:00 < bastiaan> daar was ik al aardig moe van geworden
19:00 < bastiaan> en mijn ketting is versleten
19:02 < bastiaan> en de 1 uurs was in de stromende regen
19:02 < bastiaan> gelukkig had ik de wedstrijdkoepel, dan heb je er minimaal last van
19:02 < BlondeDonder> Scalpel: weet jij hoeveel garantie er op een rohloff zit?
19:02 < bastiaan> maar toch glijd je nog wel de bocht uit als je niet op past, en stukken van het parcours waren onder water gelopen :P

Installing AWstats

August 4th, 2008

Today I decided to install AWStats for analysing my webserver traffic.

Running Debian Etch with apache 2.2 on the webserver / webproxy, the following steps are taken:

  • Install awstats with apt-get install awstats
  • Put following in /etc/awstats/awstats.conf.local:

    LoadPlugin="ipv6"
    LoadPlugin="decodeutfkeys"
  • Install the needed perl libs:
    apt-get install libnet-dns-perl libnet-ip-perl libnet-ipv6addr-perl liburi-perl
  • Create config files for each website running:
    vim /etc/awstats/awstats.www.welmers.net.conf

    Include "/etc/awstats/awstats.conf"
    
    LogFile=”/var/log/apache2/www.welmers.net-access.log”
    LogType=W
    LogFormat=1
    SiteDomain=”www.welmers.net”
    DefaultFile=”index.php index.html”
    LevelForWormsDetection=2
    

    The log format is set to 1. This is the apache combined log format, with referer URLs and browser info. Use 4 for the simple apache log format (however I would recommend combined format anyway)

  • After this, some weird bug showed up in awstats. It warns about nested includes that don’t work because perl version < 5.6 is installed. Great, I got 5.8.8. I found this bug on http://www.mail-archive.com/debian-bugs-dist%40lists.debian.org/msg385558.html and applied the patch.
  • Next thing to do is to create initial awstat databases with the logfiles I already have.
    One problem: they are alreay rotated several times. This can be solved this way:
    rm /tmp/logfile

    for i in `ls www.welmers.net-access.log.* | sort -n -r -t . -k 5`; do
    zcat $i >> /tmp/logfile;
    done

    cat www.welmers.net-access.log.1 >> /tmp/logfile
    cat www.welmers.net-access.log >> /tmp/logfile

    Edit /etc/awstats/awstats.www.welmers.net.conf to temporarily use /tmp/logfile instead of /var/log/apache2/www.welmers.net-access.log
    Then:

    perl /usr/lib/cgi-bin/awstats.pl -config=www.welmers.net -update

    And edit back /etc/awstats/awstats.www.welemrs.net.conf with the usual logfile.

  • Great. all old stuf is in awstats database! Now make it visible:

    cp /usr/share/doc/awstats/examples/apache.conf /etc/apache2/awstats.conf

    Include this file in your favorite virtual host container where awstats stuff can be viewed by authorized people only. Higly recommended, the reputation of awstats.pl CGI makes it neccessary to do so.
    After inclusion, the script alias ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ could be removed because it was already mentioned in my virtual host.
    Now, when calling https://securehost.domain.com/cgi-bin/awstats.pl?config=www.welmers.net , you should be able to see all gathered data in the log files.
  • Now we want the awstats dbases update automagicly every 10 minutes, and just before rotating the apache logfiles:
    EDITOR=vim crontab -e

    # m h dom mon dow command
    */10 * * * * for config in `ls /etc/awstats/awstats.*.conf | cut -f2,3,4 -d"."`; do perl /usr/lib/cgi-bin/awstats.pl -config=$config -update; done

    vim /etc/logrotate.d/apache2
    and add:

    prerotate
    for config in `ls /etc/awstats/awstats.*.conf | cut -f2,3,4 -d"."`; do
    perl /usr/lib/cgi-bin/awstats.pl -config=$config -update
    done
    endscript

    in the /var/log/apache2/*.log block.

Done! My statistics get updated once per 10 minutes and I can view them easily via the awstats.pl CGI with the paramater ?config=www.mysite.com

Rondrit (zuid)oost europa

August 1st, 2008

Afgelopen juli een heerlijke rondrit gehad door Duitsland, Oostenrijk, Hongarije, Slowakije en Tsjechië.
Samen met Bob Vroegh naar Hongarije gefietst naar het Fertöd-Hanság meer (op de grens met Oostenrijk) waar een NJN zomerkamp gehouden werd. Van daaruit een week de van de omgeving genoten en een dag wat meer Hongarije verkend (naar het Balatonmeer en omgeving).
De terugweg via Slowakije, Tsjechië, en zuid-oost Duitsland weer terug (inclusief steden Bratislava, Brno, Praag, Dresden, Leipzig). Al met al 3600km in 3 weken.

Foto’s: http://gallery.welmers.net/v/bastiaan/hongarije2008/
GPS tracks: http://www.welmers.net/~bastiaan/tracks/hongarije2008/

Kaart

Server Health Statistics script

July 29th, 2008

Today I wrote a script for gathering health statistics on my servers. It gets data from hddtemp and sensors and writes it to a MySQL table.

The script and how-to-use can be found on the wiki: http://wiki.welmers.net/en/Health2MySQL

The MySQL table can easily be analized by (web) scripts to graphs e.d., functionality I still need to write.

Things that already can be done is finding interresting issues like:

SELECT AVG(temp_cpu) FROM health WHERE datetime BETWEEN '2008-07-25 00:00:00' AND '2008-07-30 23:59:59'

SELECT MAX(temp_sda) FROM health

SELECT MIN(rpm_fan) FROM health